Files
BIPAuto/docs/plans/2026-03-27-auth-refactoring-implementation.md
Misaka_Company ce5be18dd7 Add auth module refactoring implementation plan
- Create detailed step-by-step implementation plan
- 9 tasks covering all refactoring aspects
- TDD approach with explicit verification steps
- Include test updates and documentation changes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 12:22:54 +08:00

573 lines
15 KiB
Markdown

# Auth Module Refactoring Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** Refactor `utils/auth.py` into a pure, decoupled authentication module with zero environment variable dependencies.
**Architecture:** Remove all environment variable reading from `auth.py`, make all parameters required, remove the `close_session()` wrapper function. Test scripts will explicitly load configuration and pass all parameters.
**Tech Stack:** Python 3.x, Playwright, pytest
---
## Task 1: Update utils/auth.py imports
**Files:**
- Modify: `utils/auth.py:1-23`
**Step 1: Remove unused imports**
Remove the following imports that are only used for environment variable access:
- `import os` (line 5)
- `from dotenv import load_dotenv` (line 10)
- `from pathlib import Path` (line 6)
The final imports should be:
```python
"""
Authentication module - Responsible for Yonyou BIP system login and logout operations
"""
from playwright.sync_api import Playwright, Browser, BrowserContext, Page, Frame
```
**Step 2: Remove module-level environment setup**
Delete lines 10-22 (BASE_DIR, load_dotenv, browsers_path configuration).
**Step 3: Verify syntax**
Run: `python -m py_compile utils/auth.py`
Expected: No syntax errors (function definitions will fail later steps, that's OK)
**Step 4: Commit**
```bash
git add utils/auth.py
git commit -m "refactor: remove environment variable imports from auth module
- Remove os, dotenv, and pathlib imports
- Remove module-level environment setup code
- Prepare for pure function implementation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
```
---
## Task 2: Refactor login() function signature
**Files:**
- Modify: `utils/auth.py:25-52`
**Step 1: Update function signature to remove default parameters**
Change the `login()` function signature from:
```python
def login(
playwright: Playwright,
username: str = None,
password: str = None,
url: str = None,
headless: bool = None,
ignore_https_errors: bool = None,
verbose: bool = True,
) -> tuple[Browser, BrowserContext, Page, Frame]:
```
To:
```python
def login(
playwright: Playwright,
username: str,
password: str,
url: str,
headless: bool,
ignore_https_errors: bool,
verbose: bool = True,
) -> tuple[Browser, BrowserContext, Page, Frame]:
```
**Step 2: Update docstring**
Replace the docstring (lines 34-52) with:
```python
"""
Login to Yonyou BIP system
Args:
playwright: Playwright instance
username: Username (required)
password: Password (required)
url: Complete login page URL (required)
headless: Whether to use headless mode (required)
ignore_https_errors: Whether to ignore HTTPS errors (required)
verbose: Whether to print detailed logs (default: True)
Returns:
tuple: (browser, context, page, main_frame)
- browser: Browser instance
- context: Browser context
- page: Page object
- main_frame: Main iframe after login (forwardFrame)
"""
```
**Step 3: Verify syntax**
Run: `python -m py_compile utils/auth.py`
Expected: No syntax errors
**Step 4: Commit**
```bash
git add utils/auth.py
git commit -m "refactor: update login() signature to require all parameters
- Remove default values for username, password, url, headless, ignore_https_errors
- Update docstring to reflect required parameters
- Maintain backward compatibility with 4-tuple return value
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
```
---
## Task 3: Remove environment variable logic from login() body
**Files:**
- Modify: `utils/auth.py:53-77`
**Step 1: Remove environment variable reading code**
Delete lines 56-76 that contain:
- `username = username or os.getenv("ERP_USERNAME")`
- `password = password or os.getenv("ERP_PASSWORD")`
- URL construction logic with `os.getenv("ERP_URL")`
- headless default handling with `os.getenv("ERP_HEADLESS")`
- ignore_https_errors default handling with `os.getenv("ERP_IGNORE_HTTPS_ERRORS")`
**Step 2: Add parameter validation**
After the imports section (after the docstring), add validation:
```python
# Validate required parameters
if not username:
raise ValueError("username is required")
if not password:
raise ValueError("password is required")
if not url:
raise ValueError("url is required")
```
**Step 3: Verify the login() function body**
The function body should now start directly with:
```python
import time
# Validate required parameters
if not username:
raise ValueError("username is required")
if not password:
raise ValueError("password is required")
if not url:
raise ValueError("url is required")
# Launch browser
browser = playwright.chromium.launch(headless=headless)
# ... rest of function unchanged
```
**Step 4: Verify syntax**
Run: `python -m py_compile utils/auth.py`
Expected: No syntax errors
**Step 5: Commit**
```bash
git add utils/auth.py
git commit -m "refactor: remove environment variable reading from login()
- Remove all os.getenv() calls from login() function
- Remove URL construction logic (caller provides complete URL)
- Add parameter validation with clear error messages
- Function is now pure with no side effects
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
```
---
## Task 4: Remove close_session() function
**Files:**
- Modify: `utils/auth.py:161-177`
**Step 1: Delete the close_session() function**
Remove lines 161-177, the entire `close_session()` function:
```python
def close_session(browser: Browser, context: BrowserContext) -> None:
"""
Close browser session
Args:
browser: Browser instance
context: Browser context
"""
# Check if auto-close browser is enabled
auto_close = os.getenv("ERP_AUTO_CLOSE_BROWSER", "true").lower() in ("true", "1", "yes")
if auto_close:
context.close()
browser.close()
else:
print("Note: Browser not auto-closed (ERP_AUTO_CLOSE_BROWSER=false)")
```
**Step 2: Verify auth.py still has only two functions**
Run: `grep "^def " utils/auth.py`
Expected output:
```
def login(
def logout(
```
**Step 3: Verify syntax**
Run: `python -m py_compile utils/auth.py`
Expected: No syntax errors
**Step 4: Commit**
```bash
git add utils/auth.py
git commit -m "refactor: remove close_session() function
- Remove close_session() wrapper function entirely
- Callers now directly manage browser/context lifecycle
- Reduces module to pure authentication functions only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
```
---
## Task 5: Update tests/test_login.py to use new API
**Files:**
- Modify: `tests/test_login.py:13,37-39,62-69`
**Step 1: Update import line**
The import on line 13 stays the same:
```python
from utils.auth import login, logout, close_session
```
Change to:
```python
from utils.auth import login, logout
```
**Step 2: Add URL construction before login() call**
After line 31 (after displaying configuration), add URL construction:
```python
# Construct complete login URL
url = f"{os.getenv('ERP_URL').rstrip('/')}/yonbip/resources/uap/rbac/login/main/index.html"
```
**Step 3: Update login() call with all required parameters**
Replace lines 37-40:
```python
browser, context, page, main_frame = login(
playwright=p,
verbose=True
)
```
With:
```python
browser, context, page, main_frame = login(
playwright=p,
username=os.getenv('ERP_USERNAME'),
password=os.getenv('ERP_PASSWORD'),
url=url,
headless=os.getenv('ERP_HEADLESS', 'false').lower() in ('true', '1', 'yes'),
ignore_https_errors=os.getenv('ERP_IGNORE_HTTPS_ERRORS', 'true').lower() in ('true', '1', 'yes'),
verbose=True
)
```
**Step 4: Replace close_session() with direct calls**
Replace lines 62-69:
```python
# Check if auto-close browser
auto_close = os.getenv("ERP_AUTO_CLOSE_BROWSER", "true").lower() in ("true", "1", "yes")
if not auto_close:
print("\n[INFO] Browser will stay open for 10 seconds for manual observation...")
time.sleep(10)
# Close browser
print("\n[CLEANUP] Closing browser session...")
close_session(browser, context)
```
With:
```python
# Check if auto-close browser
auto_close = os.getenv("ERP_AUTO_CLOSE_BROWSER", "true").lower() in ("true", "1", "yes")
if not auto_close:
print("\n[INFO] Browser will stay open for 10 seconds for manual observation...")
time.sleep(10)
# Close browser
print("\n[CLEANUP] Closing browser session...")
context.close()
browser.close()
```
**Step 5: Verify syntax**
Run: `python -m py_compile tests/test_login.py`
Expected: No syntax errors
**Step 6: Commit**
```bash
git add tests/test_login.py
git commit -m "refactor: update test_login.py to use new auth API
- Import only login and logout (remove close_session)
- Add explicit URL construction before login call
- Pass all required parameters to login()
- Replace close_session() with direct context.close() and browser.close()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
```
---
## Task 6: Run tests to verify functionality
**Files:**
- Test: `tests/test_login.py`, `utils/auth.py`
**Step 1: Activate virtual environment**
Run: `source .venv/Scripts/activate`
Expected: Command prompt shows `(.venv)`
**Step 2: Run login test**
Run: `python tests/test_login.py`
Expected: Test completes successfully with login/logout operations
**Step 3: Verify no regressions**
Check that the test output shows:
- Configuration display
- Successful login
- Successful logout
- Browser closes cleanly
**Step 4: Run auth config test**
Run: `python tests/test_auth_config.py`
Expected: Configuration check completes (no changes needed in this file)
**Step 5: Commit if any fixes needed**
If tests revealed issues:
```bash
git add tests/test_login.py utils/auth.py
git commit -m "fix: address test failures in refactored auth module
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
```
If all tests pass, no commit needed for this step.
---
## Task 7: Update CLAUDE.md documentation
**Files:**
- Modify: `CLAUDE.md:24-35,41-46`
**Step 1: Update Environment Configuration section**
Replace lines 24-35 with:
```markdown
## Environment Configuration
Configuration is loaded from `.env` file in the project root. Never commit `.env` to version control - use `.env.example` as a template.
**Environment Variables:**
- `PLAYWRIGHT_BROWSERS_PATH` - Path to Playwright browser installation
- `ERP_URL` - Base URL for the ERP system
- `ERP_USERNAME` - Login username
- `ERP_PASSWORD` - Login password
- `ERP_HEADLESS` - Whether to run browser in headless mode (true/false)
- `ERP_IGNORE_HTTPS_ERRORS` - Whether to ignore HTTPS certificate errors (true/false)
Note: Test scripts are responsible for loading environment variables and passing configuration to utility functions.
```
**Step 2: Update Module Structure section**
Replace lines 41-46 with:
```markdown
**`utils/auth.py`** - Core authentication module (pure functions)
- `login()` - Handles Yonyou BIP login with automatic force-login popup detection. Requires all parameters (username, password, url, headless, ignore_https_errors).
- `logout()` - Performs logout with confirmation dialog handling.
- Returns tuple: `(browser, context, page, main_frame)` where `main_frame` is the forwardFrame iframe.
- Callers are responsible for browser lifecycle management (context.close(), browser.close()).
```
**Step 3: Add URL Construction Pattern**
After line 52 (after Page Interaction Pattern section), add:
```markdown
### URL Construction Pattern
Callers must construct the complete login URL before passing to `login()`:
```python
url = f"{os.getenv('ERP_URL').rstrip('/')}/yonbip/resources/uap/rbac/login/main/index.html"
```
```
**Step 4: Update Code Conventions section**
Replace line 84 (Environment-First convention) with:
```markdown
2. **Environment-First**: Test scripts load environment variables and explicitly pass configuration to utility functions. No hardcoded values in code.
```
**Step 5: Verify documentation**
Run: `grep -n "os.getenv" CLAUDE.md`
Expected: Only in code examples, not as recommendations for module internals
**Step 6: Commit**
```bash
git add CLAUDE.md
git commit -m "docs: update CLAUDE.md for refactored auth module
- Clarify that auth module is pure with no environment access
- Document URL construction pattern for callers
- Update module structure documentation
- Emphasize caller responsibility for configuration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
```
---
## Task 8: Create .env.example template
**Files:**
- Create: `.env.example`
**Step 1: Create environment variable template**
Create `.env.example` with:
```bash
# Playwright Configuration
PLAYWRIGHT_BROWSERS_PATH=path/to/playwright/browsers
# ERP System Configuration
ERP_URL=https://your-erp-system.com
ERP_USERNAME=your_username
ERP_PASSWORD=your_password
# Browser Behavior
ERP_HEADLESS=false
ERP_IGNORE_HTTPS_ERRORS=true
```
**Step 2: Verify .env is in .gitignore**
Run: `grep .env .gitignore`
Expected: `.env` is listed
**Step 3: Commit**
```bash
git add .env.example
git commit -m "docs: add .env.example template
- Provide template for required environment variables
- Document all configuration options
- Help users set up their local environment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
```
---
## Task 9: Final verification and cleanup
**Files:**
- Verify: `utils/auth.py`, `tests/`, `CLAUDE.md`
**Step 1: Verify no environment imports in auth.py**
Run: `grep -E "(os\.getenv|load_dotenv)" utils/auth.py`
Expected: No matches
**Step 2: Verify all parameters are required**
Run: `grep "def login" utils/auth.py`
Expected: Function signature has no `= None` defaults except `verbose=True`
**Step 3: Run all tests**
Run: `python tests/test_login.py && python tests/test_auth_config.py`
Expected: All tests pass
**Step 4: Check documentation consistency**
Run: `grep -n "close_session" CLAUDE.md`
Expected: No mentions (function was removed)
**Step 5: Final commit if needed**
If any cleanup was done:
```bash
git add -A
git commit -m "chore: final cleanup for auth module refactoring
- Verify no environment dependencies remain
- Confirm all tests pass
- Documentation is consistent
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>"
```
**Step 6: Push to remote**
Run: `git push origin master`
Expected: All commits pushed successfully
---
## Summary
This refactoring achieves:
1. **Pure Functions**: `auth.py` has zero side effects or environment dependencies
2. **Explicit Dependencies**: All parameters required, no hidden behavior
3. **Separation of Concerns**: Configuration managed by callers, auth module handles business logic only
4. **Backward Compatible**: 4-tuple return value maintained for existing code
5. **Tested**: All tests updated and passing
**Migration Impact**: Low - only test scripts need updates, API changes are additive (making optional params required)