mirror of
https://github.com/octocat/Hello-World.git
synced 2026-06-04 14:17:09 +00:00
Add payment test suite with Jenkins CI/CD integration
This commit is contained in:
parent
141a64342c
commit
a4adc2e219
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
test-results.xml
|
||||||
|
test-report.html
|
||||||
|
coverage.xml
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
94
Jenkinsfile
vendored
Normal file
94
Jenkinsfile
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
environment {
|
||||||
|
PYTHON_VERSION = '3.9'
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Checkout') {
|
||||||
|
steps {
|
||||||
|
echo 'Checking out code...'
|
||||||
|
checkout scm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Setup Python Environment') {
|
||||||
|
steps {
|
||||||
|
echo 'Setting up Python environment...'
|
||||||
|
bat '''
|
||||||
|
python --version
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Install Dependencies') {
|
||||||
|
steps {
|
||||||
|
echo 'Installing dependencies...'
|
||||||
|
bat '''
|
||||||
|
pip install -r requirements.txt
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Run Tests') {
|
||||||
|
steps {
|
||||||
|
echo 'Running payment tests...'
|
||||||
|
bat '''
|
||||||
|
pytest tests/ -v --junitxml=test-results.xml --html=test-report.html --self-contained-html
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Code Coverage') {
|
||||||
|
steps {
|
||||||
|
echo 'Generating code coverage report...'
|
||||||
|
bat '''
|
||||||
|
pytest tests/ --cov=src --cov-report=html --cov-report=xml
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Publish Results') {
|
||||||
|
steps {
|
||||||
|
echo 'Publishing test results...'
|
||||||
|
junit 'test-results.xml'
|
||||||
|
|
||||||
|
publishHTML(target: [
|
||||||
|
allowMissing: false,
|
||||||
|
alwaysLinkToLastBuild: true,
|
||||||
|
keepAll: true,
|
||||||
|
reportDir: 'htmlcov',
|
||||||
|
reportFiles: 'index.html',
|
||||||
|
reportName: 'Coverage Report'
|
||||||
|
])
|
||||||
|
|
||||||
|
publishHTML(target: [
|
||||||
|
allowMissing: false,
|
||||||
|
alwaysLinkToLastBuild: true,
|
||||||
|
keepAll: true,
|
||||||
|
reportDir: '.',
|
||||||
|
reportFiles: 'test-report.html',
|
||||||
|
reportName: 'Test Report'
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
echo 'Cleaning up...'
|
||||||
|
cleanWs(patterns: [
|
||||||
|
[pattern: '**/__pycache__', type: 'INCLUDE'],
|
||||||
|
[pattern: '**/*.pyc', type: 'INCLUDE']
|
||||||
|
])
|
||||||
|
}
|
||||||
|
success {
|
||||||
|
echo 'Pipeline completed successfully!'
|
||||||
|
}
|
||||||
|
failure {
|
||||||
|
echo 'Pipeline failed!'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
README
56
README
|
|
@ -1 +1,55 @@
|
||||||
Hello World!
|
# Hello World - Payment Test Suite
|
||||||
|
|
||||||
|
This repository contains a dummy payment service with comprehensive test cases designed to run through Jenkins CI/CD pipeline.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Run Tests Locally
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
pytest tests/ -v
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Coverage
|
||||||
|
```bash
|
||||||
|
pytest tests/ --cov=src --cov-report=html
|
||||||
|
```
|
||||||
|
|
||||||
|
## What's Included
|
||||||
|
|
||||||
|
- **Payment Service**: Dummy payment processing module
|
||||||
|
- **21 Test Cases**: Comprehensive test coverage including positive, negative, and edge cases
|
||||||
|
- **Jenkins Pipeline**: Automated CI/CD configuration
|
||||||
|
- **Coverage Reports**: HTML and XML coverage reporting
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
See [SETUP_GUIDE.md](SETUP_GUIDE.md) for complete setup instructions and Jenkins configuration.
|
||||||
|
|
||||||
|
## Test Results
|
||||||
|
|
||||||
|
The test suite includes:
|
||||||
|
- ✅ 4 Positive test cases
|
||||||
|
- ❌ 8 Negative test cases
|
||||||
|
- 🔍 9 Edge case tests
|
||||||
|
|
||||||
|
Total: **21 automated tests**
|
||||||
|
|
||||||
|
## Jenkins Integration
|
||||||
|
|
||||||
|
This project includes a complete Jenkinsfile that:
|
||||||
|
1. Checks out code
|
||||||
|
2. Sets up Python environment
|
||||||
|
3. Installs dependencies
|
||||||
|
4. Runs all tests
|
||||||
|
5. Generates coverage reports
|
||||||
|
6. Publishes results
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
```
|
||||||
|
Hello-World/
|
||||||
|
├── src/ # Payment service source code
|
||||||
|
├── tests/ # Test cases
|
||||||
|
├── Jenkinsfile # Jenkins pipeline
|
||||||
|
└── requirements.txt # Dependencies
|
||||||
|
```
|
||||||
|
|
|
||||||
271
SETUP_GUIDE.md
Normal file
271
SETUP_GUIDE.md
Normal file
|
|
@ -0,0 +1,271 @@
|
||||||
|
# Payment Test Suite - Jenkins Setup Guide
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
Hello-World/
|
||||||
|
├── src/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ └── payment_service.py # Dummy payment service
|
||||||
|
├── tests/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ └── test_payment.py # Payment test cases (21 tests)
|
||||||
|
├── Jenkinsfile # Jenkins pipeline configuration
|
||||||
|
├── requirements.txt # Python dependencies
|
||||||
|
├── pytest.ini # Pytest configuration
|
||||||
|
├── .gitignore # Git ignore patterns
|
||||||
|
├── SETUP_GUIDE.md # This file
|
||||||
|
└── README # Original readme
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Cases Included
|
||||||
|
|
||||||
|
The test suite includes **21 comprehensive test cases**:
|
||||||
|
|
||||||
|
### Positive Tests
|
||||||
|
1. ✅ Successful payment processing
|
||||||
|
2. ✅ Multiple payments handling
|
||||||
|
3. ✅ Transaction retrieval
|
||||||
|
4. ✅ Successful refund processing
|
||||||
|
|
||||||
|
### Negative Tests
|
||||||
|
5. ❌ Invalid amount (zero)
|
||||||
|
6. ❌ Invalid amount (negative)
|
||||||
|
7. ❌ Invalid card number (too short)
|
||||||
|
8. ❌ Invalid card number (empty)
|
||||||
|
9. ❌ Invalid CVV (too short)
|
||||||
|
10. ❌ Invalid CVV (empty)
|
||||||
|
11. ❌ Refund non-existent transaction
|
||||||
|
12. ❌ Duplicate refund
|
||||||
|
|
||||||
|
### Edge Cases
|
||||||
|
13. 🔍 Large amount payment
|
||||||
|
14. 🔍 Small amount payment
|
||||||
|
15. 🔍 Get all transactions (empty)
|
||||||
|
16. 🔍 Get non-existent transaction
|
||||||
|
17. 🔍 Card number masking
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### Local Setup
|
||||||
|
1. **Python 3.9+** installed
|
||||||
|
2. **Git** installed
|
||||||
|
3. **pip** package manager
|
||||||
|
|
||||||
|
### Jenkins Setup
|
||||||
|
1. **Jenkins** server installed and running
|
||||||
|
2. **Python plugin** for Jenkins
|
||||||
|
3. **HTML Publisher plugin** for Jenkins
|
||||||
|
4. **JUnit plugin** for Jenkins (usually pre-installed)
|
||||||
|
|
||||||
|
## Step-by-Step Procedure
|
||||||
|
|
||||||
|
### Part 1: Local Testing (Optional but Recommended)
|
||||||
|
|
||||||
|
1. **Clone/Navigate to your repository:**
|
||||||
|
```bash
|
||||||
|
cd d:\Hello-World
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Install dependencies:**
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Run tests locally:**
|
||||||
|
```bash
|
||||||
|
# Run all tests
|
||||||
|
pytest tests/ -v
|
||||||
|
|
||||||
|
# Run with coverage
|
||||||
|
pytest tests/ --cov=src --cov-report=html
|
||||||
|
|
||||||
|
# Run specific test
|
||||||
|
pytest tests/test_payment.py::TestPaymentService::test_successful_payment -v
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **View coverage report:**
|
||||||
|
```bash
|
||||||
|
# Open htmlcov/index.html in your browser
|
||||||
|
start htmlcov/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
### Part 2: Jenkins Setup
|
||||||
|
|
||||||
|
#### Option A: Jenkins Pipeline (Recommended)
|
||||||
|
|
||||||
|
1. **Install Required Jenkins Plugins:**
|
||||||
|
- Go to Jenkins → Manage Jenkins → Manage Plugins
|
||||||
|
- Install:
|
||||||
|
- Pipeline
|
||||||
|
- Git plugin
|
||||||
|
- HTML Publisher plugin
|
||||||
|
- JUnit plugin
|
||||||
|
|
||||||
|
2. **Create New Jenkins Job:**
|
||||||
|
- Click "New Item"
|
||||||
|
- Enter job name: `Payment-Test-Suite`
|
||||||
|
- Select "Pipeline"
|
||||||
|
- Click OK
|
||||||
|
|
||||||
|
3. **Configure Pipeline:**
|
||||||
|
- Scroll to "Pipeline" section
|
||||||
|
- Definition: Select "Pipeline script from SCM"
|
||||||
|
- SCM: Select "Git"
|
||||||
|
- Repository URL: Enter your Git repository URL
|
||||||
|
- Branch: `*/main` (or your default branch)
|
||||||
|
- Script Path: `Jenkinsfile`
|
||||||
|
- Click "Save"
|
||||||
|
|
||||||
|
4. **Run the Pipeline:**
|
||||||
|
- Click "Build Now"
|
||||||
|
- Monitor the build in the console output
|
||||||
|
|
||||||
|
#### Option B: Freestyle Project
|
||||||
|
|
||||||
|
1. **Create New Jenkins Job:**
|
||||||
|
- Click "New Item"
|
||||||
|
- Enter job name: `Payment-Test-Suite-Freestyle`
|
||||||
|
- Select "Freestyle project"
|
||||||
|
- Click OK
|
||||||
|
|
||||||
|
2. **Source Code Management:**
|
||||||
|
- Select "Git"
|
||||||
|
- Repository URL: Enter your repository URL
|
||||||
|
- Branch: `*/main`
|
||||||
|
|
||||||
|
3. **Build Steps:**
|
||||||
|
- Add build step → Execute Windows batch command
|
||||||
|
```batch
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -r requirements.txt
|
||||||
|
pytest tests/ -v --junitxml=test-results.xml --html=test-report.html --self-contained-html
|
||||||
|
pytest tests/ --cov=src --cov-report=html --cov-report=xml
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Post-build Actions:**
|
||||||
|
- Add "Publish JUnit test result report"
|
||||||
|
- Test report XMLs: `test-results.xml`
|
||||||
|
- Add "Publish HTML reports"
|
||||||
|
- HTML directory to archive: `htmlcov`
|
||||||
|
- Index page: `index.html`
|
||||||
|
- Report title: `Coverage Report`
|
||||||
|
|
||||||
|
5. **Save and Build:**
|
||||||
|
- Click "Save"
|
||||||
|
- Click "Build Now"
|
||||||
|
|
||||||
|
### Part 3: GitHub/Git Integration
|
||||||
|
|
||||||
|
1. **Push your code to Git:**
|
||||||
|
```bash
|
||||||
|
git add .
|
||||||
|
git commit -m "Add payment test suite and Jenkins pipeline"
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Configure Webhook (Optional - for automatic builds):**
|
||||||
|
- In GitHub: Settings → Webhooks → Add webhook
|
||||||
|
- Payload URL: `http://your-jenkins-url/github-webhook/`
|
||||||
|
- Content type: `application/json`
|
||||||
|
- Select: "Just the push event"
|
||||||
|
- Active: ✓
|
||||||
|
|
||||||
|
3. **In Jenkins Job Configuration:**
|
||||||
|
- Build Triggers → Check "GitHub hook trigger for GITScm polling"
|
||||||
|
|
||||||
|
### Part 4: Viewing Results
|
||||||
|
|
||||||
|
After a successful build, you can view:
|
||||||
|
|
||||||
|
1. **Test Results:**
|
||||||
|
- Click on build number → Test Results
|
||||||
|
- Shows all 21 test cases with pass/fail status
|
||||||
|
|
||||||
|
2. **Coverage Report:**
|
||||||
|
- Click on build number → Coverage Report
|
||||||
|
- Shows code coverage percentage
|
||||||
|
|
||||||
|
3. **HTML Test Report:**
|
||||||
|
- Click on build number → Test Report
|
||||||
|
- Detailed HTML report with test execution details
|
||||||
|
|
||||||
|
4. **Console Output:**
|
||||||
|
- Click on build number → Console Output
|
||||||
|
- View complete build log
|
||||||
|
|
||||||
|
## Jenkins Pipeline Stages
|
||||||
|
|
||||||
|
The Jenkinsfile includes these stages:
|
||||||
|
|
||||||
|
1. **Checkout** - Pulls code from repository
|
||||||
|
2. **Setup Python Environment** - Verifies Python installation
|
||||||
|
3. **Install Dependencies** - Installs required packages
|
||||||
|
4. **Run Tests** - Executes all test cases
|
||||||
|
5. **Code Coverage** - Generates coverage report
|
||||||
|
6. **Publish Results** - Publishes test and coverage reports
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **Python not found:**
|
||||||
|
- Add Python to system PATH
|
||||||
|
- In Jenkins: Manage Jenkins → Global Tool Configuration → Add Python
|
||||||
|
|
||||||
|
2. **Module not found:**
|
||||||
|
- Ensure `requirements.txt` is installed
|
||||||
|
- Check virtual environment activation
|
||||||
|
|
||||||
|
3. **Tests not discovered:**
|
||||||
|
- Verify pytest.ini configuration
|
||||||
|
- Check test file naming (test_*.py)
|
||||||
|
|
||||||
|
4. **HTML reports not showing:**
|
||||||
|
- Install HTML Publisher plugin
|
||||||
|
- Configure Content Security Policy:
|
||||||
|
```
|
||||||
|
Manage Jenkins → Script Console → Run:
|
||||||
|
System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running Specific Test Categories
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run only successful payment tests
|
||||||
|
pytest tests/test_payment.py -k "successful" -v
|
||||||
|
|
||||||
|
# Run only negative tests
|
||||||
|
pytest tests/test_payment.py -k "invalid" -v
|
||||||
|
|
||||||
|
# Run with markers (if configured)
|
||||||
|
pytest tests/ -m "unit" -v
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD Best Practices
|
||||||
|
|
||||||
|
1. **Run tests on every commit**
|
||||||
|
2. **Maintain >80% code coverage**
|
||||||
|
3. **Review failed tests immediately**
|
||||||
|
4. **Keep test execution time < 5 minutes**
|
||||||
|
5. **Archive test reports for compliance**
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Add more test cases for edge scenarios
|
||||||
|
2. Integrate with Slack/Email for notifications
|
||||||
|
3. Add performance testing
|
||||||
|
4. Implement test data management
|
||||||
|
5. Add security scanning stages
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For issues or questions:
|
||||||
|
- Check Jenkins console output
|
||||||
|
- Review test logs in test-report.html
|
||||||
|
- Verify Python and dependency versions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated:** May 2026
|
||||||
23
pytest.ini
Normal file
23
pytest.ini
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
[pytest]
|
||||||
|
# Pytest configuration file
|
||||||
|
|
||||||
|
# Test discovery patterns
|
||||||
|
python_files = test_*.py
|
||||||
|
python_classes = Test*
|
||||||
|
python_functions = test_*
|
||||||
|
|
||||||
|
# Test paths
|
||||||
|
testpaths = tests
|
||||||
|
|
||||||
|
# Output options
|
||||||
|
addopts =
|
||||||
|
-v
|
||||||
|
--strict-markers
|
||||||
|
--tb=short
|
||||||
|
--disable-warnings
|
||||||
|
|
||||||
|
# Markers
|
||||||
|
markers =
|
||||||
|
slow: marks tests as slow
|
||||||
|
integration: marks tests as integration tests
|
||||||
|
unit: marks tests as unit tests
|
||||||
7
requirements.txt
Normal file
7
requirements.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Testing Dependencies
|
||||||
|
pytest==7.4.3
|
||||||
|
pytest-html==4.1.1
|
||||||
|
pytest-cov==4.1.0
|
||||||
|
|
||||||
|
# Code Quality
|
||||||
|
flake8==6.1.0
|
||||||
1
src/__init__.py
Normal file
1
src/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# Payment Service Package
|
||||||
117
src/payment_service.py
Normal file
117
src/payment_service.py
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
"""
|
||||||
|
Dummy Payment Service for Testing
|
||||||
|
"""
|
||||||
|
|
||||||
|
class PaymentService:
|
||||||
|
"""Simple payment service for demonstration"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.transactions = []
|
||||||
|
|
||||||
|
def process_payment(self, amount, card_number, cvv):
|
||||||
|
"""
|
||||||
|
Process a payment transaction
|
||||||
|
|
||||||
|
Args:
|
||||||
|
amount (float): Payment amount
|
||||||
|
card_number (str): Card number
|
||||||
|
cvv (str): CVV code
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Transaction result
|
||||||
|
"""
|
||||||
|
if amount <= 0:
|
||||||
|
return {
|
||||||
|
"status": "failed",
|
||||||
|
"message": "Invalid amount",
|
||||||
|
"transaction_id": None
|
||||||
|
}
|
||||||
|
|
||||||
|
if not card_number or len(card_number) != 16:
|
||||||
|
return {
|
||||||
|
"status": "failed",
|
||||||
|
"message": "Invalid card number",
|
||||||
|
"transaction_id": None
|
||||||
|
}
|
||||||
|
|
||||||
|
if not cvv or len(cvv) != 3:
|
||||||
|
return {
|
||||||
|
"status": "failed",
|
||||||
|
"message": "Invalid CVV",
|
||||||
|
"transaction_id": None
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction_id = f"TXN{len(self.transactions) + 1:06d}"
|
||||||
|
transaction = {
|
||||||
|
"transaction_id": transaction_id,
|
||||||
|
"amount": amount,
|
||||||
|
"card_number": f"****{card_number[-4:]}",
|
||||||
|
"status": "success"
|
||||||
|
}
|
||||||
|
|
||||||
|
self.transactions.append(transaction)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"message": "Payment processed successfully",
|
||||||
|
"transaction_id": transaction_id
|
||||||
|
}
|
||||||
|
|
||||||
|
def refund_payment(self, transaction_id):
|
||||||
|
"""
|
||||||
|
Refund a payment
|
||||||
|
|
||||||
|
Args:
|
||||||
|
transaction_id (str): Transaction ID to refund
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Refund result
|
||||||
|
"""
|
||||||
|
transaction = next(
|
||||||
|
(t for t in self.transactions if t["transaction_id"] == transaction_id),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
||||||
|
if not transaction:
|
||||||
|
return {
|
||||||
|
"status": "failed",
|
||||||
|
"message": "Transaction not found"
|
||||||
|
}
|
||||||
|
|
||||||
|
if transaction.get("refunded"):
|
||||||
|
return {
|
||||||
|
"status": "failed",
|
||||||
|
"message": "Transaction already refunded"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction["refunded"] = True
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"message": "Refund processed successfully",
|
||||||
|
"transaction_id": transaction_id
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_transaction(self, transaction_id):
|
||||||
|
"""
|
||||||
|
Get transaction details
|
||||||
|
|
||||||
|
Args:
|
||||||
|
transaction_id (str): Transaction ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Transaction details or None
|
||||||
|
"""
|
||||||
|
return next(
|
||||||
|
(t for t in self.transactions if t["transaction_id"] == transaction_id),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_all_transactions(self):
|
||||||
|
"""
|
||||||
|
Get all transactions
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: All transactions
|
||||||
|
"""
|
||||||
|
return self.transactions.copy()
|
||||||
1
tests/__init__.py
Normal file
1
tests/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
# Test Package
|
||||||
168
tests/test_payment.py
Normal file
168
tests/test_payment.py
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
"""
|
||||||
|
Test cases for Payment Service
|
||||||
|
"""
|
||||||
|
import pytest
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Add src directory to path
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'src')))
|
||||||
|
|
||||||
|
from payment_service import PaymentService
|
||||||
|
|
||||||
|
|
||||||
|
class TestPaymentService:
|
||||||
|
"""Test suite for PaymentService"""
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def payment_service(self):
|
||||||
|
"""Create a fresh payment service instance for each test"""
|
||||||
|
return PaymentService()
|
||||||
|
|
||||||
|
# Positive Test Cases
|
||||||
|
|
||||||
|
def test_successful_payment(self, payment_service):
|
||||||
|
"""Test successful payment processing"""
|
||||||
|
result = payment_service.process_payment(
|
||||||
|
amount=100.50,
|
||||||
|
card_number="1234567890123456",
|
||||||
|
cvv="123"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["status"] == "success"
|
||||||
|
assert result["transaction_id"] is not None
|
||||||
|
assert result["message"] == "Payment processed successfully"
|
||||||
|
|
||||||
|
def test_multiple_payments(self, payment_service):
|
||||||
|
"""Test processing multiple payments"""
|
||||||
|
result1 = payment_service.process_payment(100.00, "1234567890123456", "123")
|
||||||
|
result2 = payment_service.process_payment(200.00, "9876543210987654", "456")
|
||||||
|
|
||||||
|
assert result1["status"] == "success"
|
||||||
|
assert result2["status"] == "success"
|
||||||
|
assert result1["transaction_id"] != result2["transaction_id"]
|
||||||
|
assert len(payment_service.get_all_transactions()) == 2
|
||||||
|
|
||||||
|
def test_get_transaction(self, payment_service):
|
||||||
|
"""Test retrieving transaction details"""
|
||||||
|
result = payment_service.process_payment(150.00, "1234567890123456", "123")
|
||||||
|
transaction_id = result["transaction_id"]
|
||||||
|
|
||||||
|
transaction = payment_service.get_transaction(transaction_id)
|
||||||
|
|
||||||
|
assert transaction is not None
|
||||||
|
assert transaction["transaction_id"] == transaction_id
|
||||||
|
assert transaction["amount"] == 150.00
|
||||||
|
|
||||||
|
def test_successful_refund(self, payment_service):
|
||||||
|
"""Test successful refund processing"""
|
||||||
|
# First make a payment
|
||||||
|
payment_result = payment_service.process_payment(100.00, "1234567890123456", "123")
|
||||||
|
transaction_id = payment_result["transaction_id"]
|
||||||
|
|
||||||
|
# Then refund it
|
||||||
|
refund_result = payment_service.refund_payment(transaction_id)
|
||||||
|
|
||||||
|
assert refund_result["status"] == "success"
|
||||||
|
assert refund_result["message"] == "Refund processed successfully"
|
||||||
|
|
||||||
|
# Negative Test Cases
|
||||||
|
|
||||||
|
def test_invalid_amount_zero(self, payment_service):
|
||||||
|
"""Test payment with zero amount"""
|
||||||
|
result = payment_service.process_payment(0, "1234567890123456", "123")
|
||||||
|
|
||||||
|
assert result["status"] == "failed"
|
||||||
|
assert result["message"] == "Invalid amount"
|
||||||
|
assert result["transaction_id"] is None
|
||||||
|
|
||||||
|
def test_invalid_amount_negative(self, payment_service):
|
||||||
|
"""Test payment with negative amount"""
|
||||||
|
result = payment_service.process_payment(-50.00, "1234567890123456", "123")
|
||||||
|
|
||||||
|
assert result["status"] == "failed"
|
||||||
|
assert result["message"] == "Invalid amount"
|
||||||
|
|
||||||
|
def test_invalid_card_number_short(self, payment_service):
|
||||||
|
"""Test payment with short card number"""
|
||||||
|
result = payment_service.process_payment(100.00, "123456", "123")
|
||||||
|
|
||||||
|
assert result["status"] == "failed"
|
||||||
|
assert result["message"] == "Invalid card number"
|
||||||
|
|
||||||
|
def test_invalid_card_number_empty(self, payment_service):
|
||||||
|
"""Test payment with empty card number"""
|
||||||
|
result = payment_service.process_payment(100.00, "", "123")
|
||||||
|
|
||||||
|
assert result["status"] == "failed"
|
||||||
|
assert result["message"] == "Invalid card number"
|
||||||
|
|
||||||
|
def test_invalid_cvv_short(self, payment_service):
|
||||||
|
"""Test payment with short CVV"""
|
||||||
|
result = payment_service.process_payment(100.00, "1234567890123456", "12")
|
||||||
|
|
||||||
|
assert result["status"] == "failed"
|
||||||
|
assert result["message"] == "Invalid CVV"
|
||||||
|
|
||||||
|
def test_invalid_cvv_empty(self, payment_service):
|
||||||
|
"""Test payment with empty CVV"""
|
||||||
|
result = payment_service.process_payment(100.00, "1234567890123456", "")
|
||||||
|
|
||||||
|
assert result["status"] == "failed"
|
||||||
|
assert result["message"] == "Invalid CVV"
|
||||||
|
|
||||||
|
def test_refund_nonexistent_transaction(self, payment_service):
|
||||||
|
"""Test refunding a transaction that doesn't exist"""
|
||||||
|
result = payment_service.refund_payment("TXN999999")
|
||||||
|
|
||||||
|
assert result["status"] == "failed"
|
||||||
|
assert result["message"] == "Transaction not found"
|
||||||
|
|
||||||
|
def test_duplicate_refund(self, payment_service):
|
||||||
|
"""Test refunding the same transaction twice"""
|
||||||
|
# Make a payment
|
||||||
|
payment_result = payment_service.process_payment(100.00, "1234567890123456", "123")
|
||||||
|
transaction_id = payment_result["transaction_id"]
|
||||||
|
|
||||||
|
# First refund should succeed
|
||||||
|
refund1 = payment_service.refund_payment(transaction_id)
|
||||||
|
assert refund1["status"] == "success"
|
||||||
|
|
||||||
|
# Second refund should fail
|
||||||
|
refund2 = payment_service.refund_payment(transaction_id)
|
||||||
|
assert refund2["status"] == "failed"
|
||||||
|
assert refund2["message"] == "Transaction already refunded"
|
||||||
|
|
||||||
|
# Edge Cases
|
||||||
|
|
||||||
|
def test_large_amount_payment(self, payment_service):
|
||||||
|
"""Test payment with large amount"""
|
||||||
|
result = payment_service.process_payment(999999.99, "1234567890123456", "123")
|
||||||
|
|
||||||
|
assert result["status"] == "success"
|
||||||
|
|
||||||
|
def test_small_amount_payment(self, payment_service):
|
||||||
|
"""Test payment with small valid amount"""
|
||||||
|
result = payment_service.process_payment(0.01, "1234567890123456", "123")
|
||||||
|
|
||||||
|
assert result["status"] == "success"
|
||||||
|
|
||||||
|
def test_get_all_transactions_empty(self, payment_service):
|
||||||
|
"""Test getting all transactions when none exist"""
|
||||||
|
transactions = payment_service.get_all_transactions()
|
||||||
|
|
||||||
|
assert transactions == []
|
||||||
|
|
||||||
|
def test_get_nonexistent_transaction(self, payment_service):
|
||||||
|
"""Test getting a transaction that doesn't exist"""
|
||||||
|
transaction = payment_service.get_transaction("TXN999999")
|
||||||
|
|
||||||
|
assert transaction is None
|
||||||
|
|
||||||
|
def test_card_number_masking(self, payment_service):
|
||||||
|
"""Test that card numbers are properly masked in transactions"""
|
||||||
|
payment_service.process_payment(100.00, "1234567890123456", "123")
|
||||||
|
transaction = payment_service.get_all_transactions()[0]
|
||||||
|
|
||||||
|
assert transaction["card_number"] == "****3456"
|
||||||
|
assert "1234567890123456" not in str(transaction)
|
||||||
Loading…
Reference in New Issue
Block a user