Testing and Development Workflow
Purpose and Scope
This document describes the testing and development workflow for pyjiit library contributors and maintainers. It covers manual testing procedures, development patterns for adding new features, local testing strategies, and debugging approaches.
For information about the project structure and module organization, see Project Structure. For details about Poetry and dependency management, see Build System and Dependencies.
Current Testing Approach
The pyjiit library currently employs manual testing rather than an automated test suite. The primary testing mechanism is a standalone test script that validates the core authentication flow against the live JIIT Webportal API.
Test Script Architecture
The main test script is located at test_signin.py1-52 This script demonstrates the two-phase authentication process by directly calling encryption and HTTP utilities:
| Component | Purpose | Implementation |
|---|---|---|
| Environment Variables | Stores test credentials | USERNAME and PASSWORD from environment via os.getenv() |
| Payload Serialization | Encrypts login data | Uses serialize_payload() from encryption module |
| LocalName Header | Generates request headers | Uses generate_local_name() for each request |
| Two-Phase Auth | Tests complete login flow | Executes pretoken-check followed by generate-token1 |
Sources: test_signin.py1-52
Manual Testing Workflow

Diagram: Manual Testing Flow for Authentication
Sources: test_signin.py8-51
Running the Test Script
-
Set Credentials:
export UID="your_enrollment_number" export PASS="your_password"
-
Execute Test:
python test_signin.py
-
Expected Output: The script prints the complete session response including:
- Session token (JWT format)
- Member ID
- Institute information
- Registration data
Sources: test_signin.py8-12 test_signin.py49-51
Development Workflow for New Features
Adding New API Methods
When adding a new API endpoint to the Webportal class, follow the established patterns in pyjiit/wrapper.py1-489

Diagram: Standard Pattern for API Methods
Sources: pyjiit/wrapper.py173-188 pyjiit/wrapper.py191-211
Method Implementation Checklist
| Step | Action | Example Reference |
|---|---|---|
| 1. Determine authentication | Add @authenticated if session required |
pyjiit/wrapper.py156 |
| 2. Define method signature | Include typed parameters and docstring | pyjiit/wrapper.py191-197 |
| 3. Set ENDPOINT constant | Define the API path string | pyjiit/wrapper.py198 |
| 4. Build payload | Use self.session attributes as needed |
pyjiit/wrapper.py200-206 |
| 5. Serialize if needed | Call serialize_payload() for encrypted APIs |
pyjiit/wrapper.py207 |
6. Call __hit() |
Include authenticated=True and exception type | pyjiit/wrapper.py209 |
| 7. Transform response | Convert to data model or return dict | pyjiit/wrapper.py211 |
Sources: pyjiit/wrapper.py156-211
Example: Anatomy of an API Method
The get_attendance() method demonstrates all key patterns:
@authenticated
def get_attendance(self, header: AttendanceHeader, semester: Semester):
ENDPOINT = "/StudentClassAttendance/getstudentattendancedetail"
payload = {
"clientid": self.session.clientid,
"instituteid": self.session.instituteid,
"registrationcode": semester.registration_code,
"registrationid": semester.registration_id,
"stynumber": header.stynumber
}
payload = serialize_payload(payload)
resp = self.__hit("POST", API+ENDPOINT, json=payload, authenticated=True)
return resp["response"]
Key Elements:
- Uses
@authenticateddecorator (pyjiit/wrapper.py19-36) - Accesses session via
self.session(pyjiit/wrapper.py202-203) - Serializes payload for encryption (pyjiit/wrapper.py207)
- Passes
authenticated=Trueto__hit()(pyjiit/wrapper.py209)
Sources: pyjiit/wrapper.py190-211
Local Development Setup
Setting Up Development Environment
-
Clone Repository:
git clone https://github.com/codelif/pyjiit cd pyjiit
-
Install Poetry:
pip install poetry
-
Install Dependencies:
poetry install
-
Activate Virtual Environment:
poetry shell
Sources: pyproject.toml23-25
Interactive Testing
For exploratory testing of new methods or debugging:

Diagram: Interactive Testing Session
Sources: pyjiit/__init__.py pyjiit/wrapper.py70-77
Testing Individual Methods
# Example interactive testing session
from pyjiit import Webportal
wp = Webportal()
# Get and solve captcha
captcha = wp.get_captcha()
# Manually solve the captcha image
captcha.answer = "cfmab" # Your solved captcha
# Login
session = wp.student_login("enrollment_number", "password", captcha)
# Test a specific method
attendance_meta = wp.get_attendance_meta()
print(attendance_meta.headers)
print(attendance_meta.semesters)
# Test with parameters
header = attendance_meta.headers[0]
semester = attendance_meta.semesters[0]
attendance = wp.get_attendance(header, semester)
Sources: pyjiit/wrapper.py111-143 pyjiit/wrapper.py145-154 pyjiit/wrapper.py173-188
Understanding the __hit() Internal Method
The __hit() method (pyjiit/wrapper.py82-108) is the central HTTP request handler. Understanding its behavior is crucial for debugging:

Diagram: __hit() Method Error Handling Flow
Sources: pyjiit/wrapper.py82-108
Custom Exception Types
The __hit() method supports custom exceptions via the exception kwarg:
| Method | Exception Type | Usage Example |
|---|---|---|
student_login() |
LoginError |
exception=LoginError passed to __hit() |
set_password() |
AccountAPIError |
exception=AccountAPIError passed to __hit() |
| Most others | APIError (default) |
No exception kwarg needed |
Sources: pyjiit/wrapper.py83-87 pyjiit/wrapper.py130 pyjiit/wrapper.py229
Debugging Common Issues
Session Expiration
The @authenticated decorator (pyjiit/wrapper.py19-36) checks for session validity:
@authenticated
def some_method(self):
# This code only runs if self.session is not None
Note: Lines 29-32 show commented-out expiry time checking due to API bugs. Current implementation only checks for None.
Sources: pyjiit/wrapper.py19-36
Payload Serialization Errors
Not all endpoints require encrypted payloads. Compare these patterns:
| Endpoint Type | Serialization | Example |
|---|---|---|
| Encrypted | Uses serialize_payload() |
get_attendance() at pyjiit/wrapper.py207 |
| Plain JSON | Direct dict in json= |
get_student_bank_info() at pyjiit/wrapper.py168 |
Sources: pyjiit/wrapper.py157-170 pyjiit/wrapper.py190-211
LocalName Header Generation
Every request requires a LocalName header:
- Authenticated requests: Generated automatically by
session.get_headers()(pyjiit/wrapper.py61-68) - Unauthenticated requests: Generated directly by
__hit()(pyjiit/wrapper.py93)
The header is regenerated for each request using generate_local_name() from the encryption module.
Sources: pyjiit/wrapper.py61-68 pyjiit/wrapper.py89-98
Testing New Encryption-Required Endpoints
Some endpoints require payload encryption. To test these:

Diagram: Testing Encrypted Endpoint Flow
Sources: test_signin.py15-23 test_signin.py37-42
Example: Testing Encrypted Endpoint
Following the pattern from test_signin.py:
from pyjiit.encryption import serialize_payload, generate_local_name
import requests
# Build payload
payload = {
"instituteid": "your_institute_id",
"studentid": "your_student_id",
# ... other fields
}
# Encrypt payload
enc_payload = serialize_payload(payload)
# Make request
headers = {"LocalName": generate_local_name()}
url = "https://webportal.jiit.ac.in:6011/StudentPortalAPI/your/endpoint"
resp = requests.post(url, json=enc_payload, headers=headers)
# Inspect response
print(resp.json())
Sources: test_signin.py15-30
Adding Data Models
When API responses have structured data, create data model classes in the appropriate module:
| Module | Purpose | Example Classes |
|---|---|---|
pyjiit/attendance.py |
Attendance-related data | AttendanceMeta, AttendanceHeader, Semester |
pyjiit/exam.py |
Exam-related data | ExamEvent |
pyjiit/registration.py |
Registration data | RegisteredSubject, Registrations |
pyjiit/tokens.py |
Token and captcha data | Captcha, Semester |
Data Model Pattern
All data models follow this pattern (pyjiit/exam.py4-20):
from dataclasses import dataclass
@dataclass
class ModelName:
"""Class containing model description"""
field1: str
field2: int
# ... other fields
@staticmethod
def from_json(resp: dict):
return ModelName(
resp["field1key"],
resp["field2key"],
# ... map all fields
)
Sources: pyjiit/exam.py1-20 pyjiit/registration.py1-32
Development Best Practices
Method Documentation
All public methods must include docstrings following this format:
def method_name(self, param1: Type, param2: Type):
"""
:param param1: Description of parameter
:param param2: Description of parameter
:returns: Description of return value
:raises ExceptionType: When this exception is raised
"""
Sources: pyjiit/wrapper.py111-117 pyjiit/wrapper.py173-177
Error Handling Strategy

Diagram: Exception Handling in API Methods
Sources: pyjiit/wrapper.py82-108
Session Attribute Usage
Common session attributes used in payload construction:
| Attribute | Source | Usage |
|---|---|---|
self.session.clientid |
pyjiit/wrapper.py57 | Client identification |
self.session.instituteid |
pyjiit/wrapper.py48 | Institute identification |
self.session.memberid |
pyjiit/wrapper.py49 | Student ID |
self.session.membertype |
pyjiit/wrapper.py58 | Member type (student) |
self.session.token |
pyjiit/wrapper.py53 | JWT authentication token |
Sources: pyjiit/wrapper.py42-59
Workflow Summary

Diagram: Complete Development and Release Workflow
Sources: pyproject.toml1-28 test_signin.py1-52
Future Testing Enhancements
Currently, pyjiit does not have automated unit or integration tests. Potential improvements include:
- Mock API Testing: Create mock responses for testing without live API access
- pytest Integration: Add pytest framework with test fixtures for common scenarios
- CI Test Stage: Add GitHub Actions workflow to run tests on pull requests
- Coverage Reporting: Track test coverage for core functionality
- Integration Tests: Test complete workflows (login → fetch data → logout)
These enhancements would require:
- Adding
pytestto development dependencies in pyproject.toml19-21 - Creating a
tests/directory with test modules - Mocking the JIIT API responses to avoid requiring real credentials
Sources: pyproject.toml19-21