first commit
Verify / test (push) Failing after 2m54s

This commit is contained in:
2026-11-05 17:31:17 +03:30
کامیت 56c5ee23d3
63فایلهای تغییر یافته به همراه2749 افزوده شده و 0 حذف شده
+5
مشاهده پرونده
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
PYTHONPATH=backend .venv/bin/python -m pytest backend/tests -q
npm test
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
"""Concurrent actual HTTP uploads; run only against a disposable QA deployment."""
import argparse
from concurrent.futures import ThreadPoolExecutor
import json
import os
from pathlib import Path
import time
import httpx
p=argparse.ArgumentParser();p.add_argument('file',type=Path);p.add_argument('--url',default='http://127.0.0.1:8000');p.add_argument('--count',type=int,default=8);a=p.parse_args()
raw=a.file.read_bytes();key=os.environ['API_KEY']
def one(_):
with httpx.Client(base_url=a.url,timeout=30) as client:
response=client.post('/api/v1/schematics/render',files={'file':(a.file.name,raw)},headers={'X-API-Key':key})
assert response.status_code==202,response.text
return response.json()
with ThreadPoolExecutor(max_workers=a.count) as pool: jobs=list(pool.map(one,range(a.count)))
assert len({job['id'] for job in jobs})==a.count
with httpx.Client(base_url=a.url,timeout=30) as client:
for job in jobs:
deadline=time.monotonic()+90
while time.monotonic()<deadline:
body=client.get(f"/api/v1/schematics/{job['id']}/status",headers={'X-Viewer-Token':job['viewer_token']}).json()
if body['status'] in ('completed','failed'):break
time.sleep(.2)
assert body['status']=='completed',body
print(json.dumps({'concurrent_uploads':a.count,'unique_ids':a.count,'completed':a.count}))
+24
مشاهده پرونده
@@ -0,0 +1,24 @@
#!/usr/bin/env python3
"""Audit a directory of real inputs without copying design files into source."""
import argparse
import hashlib
import json
import sys
from pathlib import Path
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'backend'))
from app.parser.schdoc_parser import parse_schdoc
from app.render import svg_document
p=argparse.ArgumentParser();p.add_argument('directory',type=Path);p.add_argument('--output',type=Path,default=Path('validation-output/corpus.json'));a=p.parse_args()
results=[]
for path in sorted(a.directory.glob('*')):
if path.suffix.lower()!='.schdoc': continue
raw=path.read_bytes()
result={'filename':path.name,'sha256':hashlib.sha256(raw).hexdigest()}
try:
doc=parse_schdoc(raw,path.name,'audit'); svg=svg_document(doc)
result.update(status='parsed',record_count=doc.metadata['record_count'],primitive_count=len(doc.primitives),component_count=len(doc.components),svg_bytes=len(svg),diagnostics=[d.model_dump() for d in doc.diagnostics])
except Exception as e: result.update(status='failed',error=str(e))
results.append(result)
a.output.parent.mkdir(parents=True,exist_ok=True);a.output.write_text(json.dumps(results,indent=2))
print(json.dumps({'files':len(results),'parsed':sum(r['status']=='parsed' for r in results),'failed':sum(r['status']=='failed' for r in results),'output':str(a.output)}))
sys.exit(1 if not results or any(r['status']=='failed' for r in results) else 0)
Executable
+9
مشاهده پرونده
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
export PYTHONPATH="$PWD/backend"
if [[ -f .env ]]; then set -a; source .env; set +a; fi
.venv/bin/python -m app.worker &
worker_pid=$!
trap 'kill "$worker_pid" 2>/dev/null || true' EXIT INT TERM
.venv/bin/python -m uvicorn app.main:app --host 127.0.0.1 --port 8000 --no-access-log
+14
مشاهده پرونده
@@ -0,0 +1,14 @@
#!/usr/bin/env python3
"""Fetch a real upstream native SchDoc; checksum-pin it and never redistribute."""
import hashlib
from pathlib import Path
from urllib.request import urlopen
URL='https://raw.githubusercontent.com/KiCad/kicad-source-mirror/master/qa/data/eeschema/plugins/altium/issue24861/Channel.SchDoc'
SHA='e7dc42a4e983e6b716f6883eb0863fe47164d941636e11b1f2b5ec5d0143dca4'
root=Path(__file__).resolve().parents[1]
target=root/'.runtime/fixtures/Channel.SchDoc'
if not target.exists() or hashlib.sha256(target.read_bytes()).hexdigest()!=SHA:
with urlopen(URL,timeout=30) as response: data=response.read(4*1024*1024)
if hashlib.sha256(data).hexdigest()!=SHA: raise SystemExit('Upstream fixture changed; review before updating expected hash')
target.parent.mkdir(parents=True,exist_ok=True); target.write_bytes(data)
print(target)
+35
مشاهده پرونده
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
"""Exercise the actual HTTP API and worker using an actual native file."""
import argparse
import json
import os
from pathlib import Path
import time
import httpx
p=argparse.ArgumentParser()
p.add_argument('file',type=Path)
p.add_argument('--url',default='http://127.0.0.1:8000')
a=p.parse_args()
key=os.getenv('API_KEY','')
with httpx.Client(base_url=a.url,timeout=30) as client:
response=client.post('/api/v1/schematics/render',files={'file':(a.file.name,a.file.read_bytes())},headers={'X-API-Key':key})
assert response.status_code==202,response.text
body=response.json(); id=body['id']; headers={'X-Viewer-Token':body['viewer_token']}
base=f'/api/v1/schematics/{id}'
assert client.get(base+'/data').status_code==404
deadline=time.monotonic()+90
while time.monotonic()<deadline:
status=client.get(base+'/status',headers=headers).json()
if status['status'] in ('completed','failed'): break
time.sleep(.5)
assert status['status']=='completed',status
data=client.get(base+'/data',headers=headers)
assert data.status_code==200 and data.json()['metadata']['format']=='ole'
svg=client.get(base+'/svg',headers=headers)
assert svg.status_code==200 and '<svg' in svg.text
assert client.get('/view/'+id).status_code==200
assert client.get('/docs').status_code==200
print(json.dumps({'id':id,'status':status['status'],'compatibility':status['compatibility'],
'records':status['record_count'],'primitives':status['primitive_count'],
'private_access_enforced':True,'svg_bytes':len(svg.content)},indent=2))
+61
مشاهده پرونده
@@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""Render actual SchDoc and compare to a trusted Altium page PNG, without alignment.
Images must represent the same sheet viewport. No registration, recoloring or
cropping hides coordinate differences. Altium PDF exports can be rasterized to
PNG with pdftoppm first. This tool does not manufacture reference images.
"""
import argparse
import hashlib
import json
import sys
from pathlib import Path
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'backend'))
import cairosvg
import numpy as np
from PIL import Image
from app.parser.schdoc_parser import parse_schdoc
from app.render import svg_document
def main():
p=argparse.ArgumentParser(description=__doc__)
p.add_argument('schematic',type=Path)
p.add_argument('--reference',type=Path)
p.add_argument('--output',type=Path,default=Path('validation-output'))
p.add_argument('--width',type=int,default=2000)
p.add_argument('--max-mae',type=float,default=2.0,help='Mean absolute RGB error threshold, 0..255')
p.add_argument('--max-changed-fraction',type=float,default=.01)
p.add_argument('--pixel-tolerance',type=int,default=12)
args=p.parse_args(); args.output.mkdir(parents=True,exist_ok=True)
raw=args.schematic.read_bytes(); doc=parse_schdoc(raw,args.schematic.name,'validation')
svg=svg_document(doc); (args.output/'drawing.svg').write_bytes(svg)
width=args.width; height=max(1,round(width*doc.sheet.bounds.height/doc.sheet.bounds.width))
if args.reference:
with Image.open(args.reference) as ref: width,height=ref.size
expected_ratio=doc.sheet.bounds.width/doc.sheet.bounds.height
if abs(width/height/expected_ratio-1)>.005:
p.error('Reference aspect ratio differs from original sheet; export the same viewport')
png=cairosvg.svg2png(bytestring=svg,output_width=width,output_height=height)
(args.output/'rendered.png').write_bytes(png)
report={'source_sha256':hashlib.sha256(raw).hexdigest(),'record_count':doc.metadata['record_count'],
'component_count':len(doc.components),'primitive_count':len(doc.primitives),
'diagnostics':[d.model_dump() for d in doc.diagnostics],
'reference_compared':bool(args.reference),'fidelity_verified':False}
passed=True
if args.reference:
reference=Image.open(args.reference).convert('RGB'); actual=Image.open(args.output/'rendered.png').convert('RGB')
a=np.array(actual,dtype=np.int16); b=np.array(reference,dtype=np.int16); delta=np.abs(a-b)
mae=float(delta.mean()); changed=float((delta.max(axis=2)>args.pixel_tolerance).mean())
heat=np.zeros_like(a,dtype=np.uint8); heat[:,:,0]=np.clip(delta.max(axis=2)*4,0,255).astype(np.uint8)
Image.fromarray(heat).save(args.output/'difference.png')
Image.blend(reference,actual,.5).save(args.output/'overlay.png')
passed=mae<=args.max_mae and changed<=args.max_changed_fraction
report.update(reference_sha256=hashlib.sha256(args.reference.read_bytes()).hexdigest(),mae=mae,
changed_pixel_fraction=changed,thresholds={'max_mae':args.max_mae,'max_changed_fraction':args.max_changed_fraction,'pixel_tolerance':args.pixel_tolerance},
regression_passed=passed)
(args.output/'report.json').write_text(json.dumps(report,indent=2))
print(json.dumps({k:v for k,v in report.items() if k!='diagnostics'},indent=2))
return 0 if passed else 1
if __name__=='__main__': sys.exit(main())