Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
0609749
Initial commit
vinod-ship-it Dec 27, 2025
a50e241
Initial commit
vinod-ship-it Dec 27, 2025
28e23f2
Initial commit
vinod-ship-it Dec 27, 2025
ee6ebeb
Initial commit
vinod-ship-it Dec 27, 2025
1246189
Initial commit
vinod-ship-it Dec 27, 2025
c238d85
Initial commit
vinod-ship-it Dec 28, 2025
7ed54e7
Initial commit
vinod-ship-it Dec 28, 2025
8c88bc1
Initial commit
vinod-ship-it Dec 29, 2025
0ae41ce
Initial commit
vinod-ship-it Dec 31, 2025
1667eb5
Initial commit
vinod-ship-it Dec 31, 2025
c521ffe
Initial commit
vinod-ship-it Dec 31, 2025
41241a7
Initial commit
vinod-ship-it Jan 2, 2026
f1ca997
Initial commit
vinod-ship-it Jan 2, 2026
d647ee5
Initial commit
vinod-ship-it Jan 2, 2026
f52bbfc
Initial commit
vinod-ship-it Jan 2, 2026
6fc2515
Initial commit
vinod-ship-it Jan 7, 2026
0e9311f
Initial commit
vinod-ship-it Jan 14, 2026
7b96b8f
Initial commit
vinod-ship-it Jan 14, 2026
1072ab7
Initial commit
vinod-ship-it Jan 14, 2026
2d0eeff
Initial commit
vinod-ship-it Jan 14, 2026
347a0a5
Initial commit
vinod-ship-it Jan 14, 2026
58ca3ef
Initial commit
vinod-ship-it Jan 14, 2026
1c7e099
Initial commit
vinod-ship-it Jan 14, 2026
adbf9c4
Initial commit
vinod-ship-it Jan 15, 2026
cabee29
Initial commit
vinod-ship-it Jan 15, 2026
a3f0c38
Initial commit
vinod-ship-it Jan 15, 2026
a788bff
Initial commit
vinod-ship-it Jan 15, 2026
3238c78
Initial commit
vinod-ship-it Jan 16, 2026
633d878
Initial commit
vinod-ship-it Jan 16, 2026
6f3db4b
Initial commit
vinod-ship-it Jan 16, 2026
b858841
Initial commit
vinod-ship-it Jan 17, 2026
7a20ba0
Initial commit
vinod-ship-it Jan 17, 2026
6788bd9
Initial commit
vinod-ship-it Jan 17, 2026
57987f5
Initial commit
vinod-ship-it Jan 17, 2026
98ebb8c
Initial commit
vinod-ship-it Jan 17, 2026
03a51e1
Initial commit
vinod-ship-it Jan 17, 2026
2946b9e
Initial commit
vinod-ship-it Jan 17, 2026
6ed752e
Initial commit
vinod-ship-it Jan 17, 2026
17dd84b
Initial commit
vinod-ship-it Jan 17, 2026
503592d
Initial commit
vinod-ship-it Jan 18, 2026
dbcd1dc
Initial commit
vinod-ship-it Jan 18, 2026
0033847
Initial commit
vinod-ship-it Jan 18, 2026
ffce1ea
Initial commit
vinod-ship-it Jan 18, 2026
276fc2b
Initial commit
vinod-ship-it Jan 18, 2026
6a7ace0
Initial commit
vinod-ship-it Jan 18, 2026
d8c2f91
Initial commit
vinod-ship-it Jan 18, 2026
1984157
Initial commit
vinod-ship-it Jan 18, 2026
ac0f942
Initial commit
vinod-ship-it Jan 20, 2026
833d692
Initial commit
vinod-ship-it Jan 21, 2026
12929c3
DO NOT CHECKIN
vinod-ship-it Jan 22, 2026
b3a6287
v1.0.8 changes - config operations spec, jwt token file changes
dchotwani-panw Feb 5, 2026
686f30b
Initial commit
vinod-ship-it Feb 9, 2026
a97c9b2
Initial commit
vinod-ship-it Feb 9, 2026
4ff5a9f
ike gateway fixes for ip
dchotwani-panw Feb 9, 2026
5534b5e
github actions to run tests
dchotwani-panw Feb 11, 2026
9455639
Initial commit
vinod-ship-it Feb 12, 2026
2b6bb78
use cleint credentials form env not config file for github action
dchotwani-panw Feb 12, 2026
3dea50b
fetch by name method
dchotwani-panw Feb 12, 2026
9c02d71
updated github action
dchotwani-panw Feb 12, 2026
9a88ab6
add 'default' to API_I00013 error code
dchotwani-panw Feb 12, 2026
0cc3c57
updated test for app override rules
dchotwani-panw Feb 12, 2026
e71da05
fix application override test
dchotwani-panw Feb 13, 2026
b1d1689
Initial commit
vinod-ship-it Feb 14, 2026
c8bda63
updated ci/cd tests
dchotwani-panw Feb 14, 2026
c34fb07
Initial commit
vinod-ship-it Feb 22, 2026
e521e35
Initial commit
vinod-ship-it Feb 22, 2026
c05ce10
Initial commit
vinod-ship-it Feb 23, 2026
020a90e
Initial commit
vinod-ship-it Feb 23, 2026
2ed3047
Initial commit
vinod-ship-it Feb 23, 2026
6051721
Initial commit
vinod-ship-it Feb 23, 2026
d8aef2f
Initial commit
vinod-ship-it Feb 23, 2026
3950852
Initial commit
vinod-ship-it Feb 23, 2026
dd85c9d
Initial commit
vinod-ship-it Feb 23, 2026
308b5cd
Initial commit
vinod-ship-it Feb 23, 2026
b82fd1b
Initial commit
vinod-ship-it Feb 23, 2026
6c7dfae
Initial commit
vinod-ship-it Feb 24, 2026
f5a8c5d
Initial commit
vinod-ship-it Feb 26, 2026
78981db
Initial commit
vinod-ship-it Mar 2, 2026
c5d2779
Initial commit
vinod-ship-it Mar 2, 2026
5fb48bd
Initial commit
vinod-ship-it Mar 2, 2026
9290508
Initial commit
vinod-ship-it Mar 2, 2026
ce16b6f
Initial commit
vinod-ship-it Mar 3, 2026
7ee0d44
Initial commit
vinod-ship-it Mar 3, 2026
451ea15
Initial commit
vinod-ship-it Mar 4, 2026
a9b3a05
Initial commit
vinod-ship-it Mar 5, 2026
3bac532
Initial commit
vinod-ship-it Mar 5, 2026
fd4ae36
added trace and request ids for ci/cd
dchotwani-panw Mar 5, 2026
7cda2ce
added header logging call for error decorator
dchotwani-panw Mar 5, 2026
dd3581b
fix function names
dchotwani-panw Mar 9, 2026
78f0a7b
spec updates
dchotwani-panw Mar 13, 2026
56a99c3
v1.0.9 new resources
dchotwani-panw Mar 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
364 changes: 364 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,364 @@
name: Run Tests

on:
push:
branches:
- develop
pull_request:
branches:
- develop

jobs:
test:
name: Test ${{ matrix.service }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 1
matrix:
service:
- objects
- network_services
- security_services
- identity_services
- deployment_services
- config_setup
- config_operations
- device_settings

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
pip install pytest pytest-cov pytest-json-report

- name: Create SCM config file from secrets
run: |
mkdir -p config
cat > config/scm-config.json << 'EOF'
{
"auth_url": "${{ secrets.SCM_AUTH_URL }}",
"client_id": "${{ secrets.SCM_CLIENT_ID }}",
"client_secret": "${{ secrets.SCM_CLIENT_SECRET }}",
"host": "${{ secrets.SCM_HOST }}",
"protocol": "${{ secrets.SCM_PROTOCOL }}",
"scope": "${{ secrets.SCM_SCOPE }}",
"logging": "quiet",
"skip_verify_certificate": ${{ secrets.SCM_SKIP_VERIFY_CERTIFICATE }}
}
EOF

- name: Run tests for ${{ matrix.service }}
env:
SCM_CLIENT_ID: ${{ secrets.SCM_CLIENT_ID }}
SCM_CLIENT_SECRET: ${{ secrets.SCM_CLIENT_SECRET }}
SCM_SCOPE: ${{ secrets.SCM_SCOPE }}
SCM_TSG_ID: ${{ secrets.SCM_TSG_ID }}
SCM_HOST: ${{ secrets.SCM_HOST }}
SCM_AUTH_URL: ${{ secrets.SCM_AUTH_URL }}
PYTHONWARNINGS: ignore
SCM_LOGGING: quiet
run: |
set +e

pytest -v -rs --json-report --json-report-file=test-report.json scm/${{ matrix.service }}/tests/
exit_code=$?

# Parse JSON report and create detailed markdown summary
cat > parse_pytest.py << 'PYTHON'
import json
import sys
import os

try:
with open('test-report.json', 'r') as f:
report = json.load(f)
except FileNotFoundError:
print("No test report found")
# Write empty stats
stats = {
"package": "${{ matrix.service }}",
"passed": 0,
"failed": 0,
"skipped": 0,
"errored": 0,
"total": 0
}
with open('test-stats-${{ matrix.service }}.json', 'w') as f:
json.dump(stats, f, indent=2)
sys.exit(0)

tests = report.get('tests', [])

passed = [t for t in tests if t.get('outcome') == 'passed']
failed = [t for t in tests if t.get('outcome') == 'failed']
skipped = [t for t in tests if t.get('outcome') == 'skipped']
errored = [t for t in tests if t.get('outcome') == 'error']

# Print summary to console
print(f"\n{'='*60}")
print(f"Test Results: ${{ matrix.service }}")
print(f"{'='*60}")
print(f"Passed: {len(passed)}")
print(f"Failed: {len(failed)}")
print(f"Errors: {len(errored)}")
print(f"Skipped: {len(skipped)}")
print(f"{'='*60}\n")

# Write detailed markdown to file
with open('test-detail-${{ matrix.service }}.md', 'w') as f:
f.write(f'<a name="${{ matrix.service }}"></a>\n\n')
f.write(f"## ${{ matrix.service }}\n\n")
f.write(f"**{len(passed)} passed, {len(failed)} failed, {len(errored)} errors, {len(skipped)} skipped**\n\n")

if failed or errored:
f.write("### Failed Tests\n\n")
for test in failed + errored:
test_name = test.get('nodeid', 'Unknown')
duration = test.get('duration', 0)
outcome = test.get('outcome', 'failed')
f.write(f"#### `{test_name}` ({duration:.2f}s) [{outcome}]\n\n")

# Extract error information from call or setup phase
call = test.get('call', {})
setup = test.get('setup', {})
longrepr = call.get('longrepr', '') or setup.get('longrepr', '')

# Also capture stdout which contains our header logging
stdout = call.get('stdout', '') or setup.get('stdout', '')

if longrepr or stdout:
f.write("<details>\n<summary>Error Details</summary>\n\n")
f.write("```\n")

# Include stdout first (contains X-Request-ID, X-Trace-ID, etc.)
if stdout:
stdout_lines = stdout.strip().split('\n')
# Look for our header block in stdout
for line in stdout_lines:
if any(keyword in line for keyword in ['API RESPONSE HEADERS', 'API ERROR RESPONSE', 'X-Request-ID:', 'X-Trace-ID:', 'Status Code:', 'Error Body:']):
f.write(line + '\n')

# Include traceback
if longrepr:
lines = longrepr.split('\n')
f.write('\n'.join(lines[-20:]))

f.write("\n```\n")
f.write("</details>\n\n")
else:
f.write("_No detailed error output captured_\n\n")

f.write("---\n\n")

if passed:
f.write("<details>\n<summary>Passed Tests ({} tests)</summary>\n\n".format(len(passed)))
f.write("| Test Name | Duration |\n")
f.write("|-----------|----------|\n")
for test in passed:
test_name = test.get('nodeid', 'Unknown')
duration = test.get('duration', 0)
f.write(f"| `{test_name}` | {duration:.2f}s |\n")
f.write("\n</details>\n\n")

if skipped:
f.write("<details>\n<summary>Skipped Tests ({} tests)</summary>\n\n".format(len(skipped)))
f.write("| Test Name | Reason |\n")
f.write("|-----------|--------|\n")
for test in skipped:
test_name = test.get('nodeid', 'Unknown')
setup = test.get('setup', {})
longrepr = setup.get('longrepr', 'No reason provided')
skip_msg = longrepr.split('\n')[-1] if longrepr else 'Skipped'
f.write(f"| `{test_name}` | {skip_msg[:100]} |\n")
f.write("\n</details>\n\n")

# Write stats JSON for the unified summary table
stats = {
"package": "${{ matrix.service }}",
"passed": len(passed),
"failed": len(failed),
"skipped": len(skipped),
"errored": len(errored),
"total": len(passed) + len(failed) + len(skipped) + len(errored)
}
with open('test-stats-${{ matrix.service }}.json', 'w') as f:
json.dump(stats, f, indent=2)
PYTHON

python3 parse_pytest.py

if [ $exit_code -ne 0 ]; then
echo "Tests failed for ${{ matrix.service }}"
exit $exit_code
else
echo "All tests passed for ${{ matrix.service }}"
fi
timeout-minutes: 45

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.service }}
path: |
test-stats-${{ matrix.service }}.json
test-detail-${{ matrix.service }}.md
retention-days: 1
overwrite: true

summary:
name: Test Summary
runs-on: ubuntu-latest
needs: test
if: always()
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Download all test results
uses: actions/download-artifact@v4
with:
pattern: test-results-*
path: test-results

- name: Generate unified summary
run: |
cat > generate_summary.py << 'PYTHON'
import json, os, glob, re

summary = []

# -- Part 1: Unified results table (TOP) --
stats_files = sorted(glob.glob('test-results/test-results-*/test-stats-*.json'))
packages = []
total_passed = 0
total_failed = 0
total_skipped = 0
total_errored = 0
total_tests = 0

for f in stats_files:
with open(f) as fh:
data = json.load(fh)
packages.append(data)
total_passed += data['passed']
total_failed += data['failed']
total_skipped += data['skipped']
total_errored += data.get('errored', 0)
total_tests += data['total']

summary.append("# Test Summary\n")
summary.append("## Results by Package\n")
summary.append("| Package | Passed | Failed | Errors | Skipped | Total | Status |")
summary.append("|---------|--------|--------|--------|---------|-------|--------|")

for pkg in packages:
errored = pkg.get('errored', 0)
status = "PASS" if pkg['failed'] == 0 and errored == 0 else "FAIL"
anchor = pkg['package']
summary.append(
f"| [`{pkg['package']}`](#user-content-{anchor}) | {pkg['passed']} | {pkg['failed']} | "
f"{errored} | {pkg['skipped']} | {pkg['total']} | {status} |"
)

overall_status = "All Passing" if total_failed == 0 and total_errored == 0 else f"{total_failed} Failures, {total_errored} Errors"
summary.append(
f"| **Total** | **{total_passed}** | **{total_failed}** | "
f"**{total_errored}** | **{total_skipped}** | **{total_tests}** | **{overall_status}** |"
)
summary.append("")

# -- Part 2: Per-package details (MIDDLE) --
summary.append("---\n")
summary.append("## Package Details\n")

detail_files = sorted(glob.glob('test-results/test-results-*/test-detail-*.md'))
for f in detail_files:
with open(f) as fh:
summary.append(fh.read())

# -- Part 3: Missing test coverage (BOTTOM) --
summary.append("---\n")
summary.append("## Test Coverage Gaps\n")

missing_any = False
coverage_rows = []

for pkg_dir in sorted(glob.glob('scm/*/')):
pkg_name = os.path.basename(pkg_dir.rstrip('/'))

# Skip non-package directories
api_dir = os.path.join(pkg_dir, 'api')
test_dir = os.path.join(pkg_dir, 'tests')
if not os.path.isdir(api_dir):
continue

# Python API files: scm/<pkg>/api/<resource>_api.py
api_files = glob.glob(os.path.join(api_dir, '*_api.py'))
# Python test files: scm/<pkg>/tests/api_<resource>_test.py
test_files = glob.glob(os.path.join(test_dir, 'api_*_test.py')) if os.path.isdir(test_dir) else []

skip_names = {'__init__'}

api_names = set()
for af in api_files:
base = os.path.basename(af)
name = base.replace('_api.py', '')
if name in skip_names:
continue
api_names.add(name)

test_names = set()
for tf in test_files:
base = os.path.basename(tf)
name = base.replace('api_', '').replace('_test.py', '')
test_names.add(name)

missing = api_names - test_names
if missing:
missing_any = True
for m in sorted(missing):
api_path = os.path.join(api_dir, f'{m}_api.py')
methods = []
if os.path.exists(api_path):
with open(api_path) as fh:
content = fh.read()
for method in ['create_', 'list_', 'get_', 'update_', 'delete_', 'fetch_']:
if re.search(rf'def {method}{re.escape(m)}\b', content):
methods.append(method.rstrip('_').capitalize())
method_str = ', '.join(methods) if methods else 'unknown'
coverage_rows.append(f"| `{pkg_name}` | `{m}` | {method_str} |")

if missing_any:
summary.append("APIs with **no test file** (have implementation but no `tests/api_*_test.py`):\n")
summary.append("| Package | API | Available Methods |")
summary.append("|---------|-----|-------------------|")
summary.extend(coverage_rows)
else:
summary.append("All API implementations have corresponding test files.")

summary.append("")

# Write to GITHUB_STEP_SUMMARY
summary_path = os.environ.get('GITHUB_STEP_SUMMARY', '/dev/stdout')
with open(summary_path, 'w') as f:
f.write('\n'.join(summary) + '\n')

print('\n'.join(summary))
PYTHON
python3 generate_summary.py

- name: Propagate failure
if: needs.test.result == 'failure'
run: exit 1
Loading
Loading