test: prepare phase 9 requalification harness and blocker register
MariaDB persistence qualification / mariadb (push) Failing after 7s
MariaDB persistence qualification / mariadb (push) Failing after 7s
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
"""Disposable local Linux renderer; no host daemon is exposed to the API container."""
|
||||
import datetime, json, os, re, subprocess, sys, time, urllib.request, uuid
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / '.artifacts/p9-requal-01/linux'
|
||||
IMAGE = 'jobtracker-p9-requal-api:security-check'
|
||||
DB = 'jobtracker-it-p9-requal-mariadb-test-1'
|
||||
PIN = 'mariadb:12.2.2@sha256:42a27baaba3e8f3f0159d16381d5a7156661aa91dcc65f663cabb6a6f9503c00'
|
||||
VERSION = 'p9-requal-linux-780b9bc'
|
||||
|
||||
def guard_schema(name):
|
||||
if not re.fullmatch(r'jt_it_[0-9a-f]{32}', name):
|
||||
raise ValueError('Only a generated qualification schema is allowed.')
|
||||
return name
|
||||
|
||||
def command(args, **kwargs):
|
||||
return subprocess.run(['rtk', 'proxy', *args], check=True, capture_output=True, text=True, encoding='utf-8', **kwargs).stdout.strip()
|
||||
|
||||
def sql(query):
|
||||
return command(['docker', 'exec', '-e', 'MYSQL_PWD=jt-integration-only', DB,
|
||||
'mariadb', '-uroot', '--batch', '--skip-column-names', '-e', query])
|
||||
|
||||
def safe_error(error):
|
||||
# CalledProcessError includes command arguments, including development-only credentials.
|
||||
return str(error).replace('jt-integration-only','[test-password]').replace('E2ePassword123!','[test-password]').replace('e2e-only-key-at-least-thirty-two-characters-long','[test-key]')
|
||||
|
||||
def main():
|
||||
if '--self-test' in sys.argv:
|
||||
assert guard_schema('jt_it_'+'a'*32)
|
||||
for name in ['jobtracker', 'jt_it_abc', 'jt_it_'+'a'*32+';DROP DATABASE x']:
|
||||
try: guard_schema(name)
|
||||
except ValueError: continue
|
||||
raise AssertionError(name)
|
||||
print('Linux qualification schema guards: PASS')
|
||||
return
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
if (OUT/'process.json').exists() or (OUT.parent/'linux-renderer.json').exists():
|
||||
raise ValueError('Retain prior evidence; select a fresh run directory.')
|
||||
owned = json.loads(command(['docker','inspect',DB]))[0]
|
||||
assert owned['Config']['Image'] == PIN
|
||||
assert owned['Config']['Labels']['com.docker.compose.project'] == 'jobtracker-it-p9-requal'
|
||||
assert owned['Config']['Labels']['com.docker.compose.service'] == 'mariadb-test'
|
||||
assert '/var/lib/mysql' in owned['HostConfig'].get('Tmpfs',{})
|
||||
assert not owned['HostConfig'].get('Privileged') and owned['HostConfig'].get('NetworkMode')!='host'
|
||||
assert owned['State']['Running'] and owned['State']['Health']['Status']=='healthy'
|
||||
assert owned['HostConfig']['PortBindings']['3306/tcp'] == [{'HostIp':'127.0.0.1','HostPort':'33308'}]
|
||||
actual = sql('SELECT VERSION()')
|
||||
assert actual.startswith('12.2.2-MariaDB'), actual
|
||||
name=guard_schema('jt_it_'+uuid.uuid4().hex)
|
||||
suffix=uuid.uuid4().hex[:12]
|
||||
api='jobtracker-p9-requal-linux-'+suffix
|
||||
network='jobtracker-p9-requal-linux-'+suffix
|
||||
record={'startedUtc':datetime.datetime.now(datetime.timezone.utc).isoformat(), 'schema':name,
|
||||
'serverVersion':actual,'imageTag':IMAGE,'apiContainer':api,'network':network,'cleanup':{},'exitCode':1}
|
||||
created=attached=network_created=api_created=False
|
||||
try:
|
||||
assert sql(f"SELECT COUNT(*) FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='{name}'")=='0'
|
||||
sql(f'CREATE DATABASE `{name}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci')
|
||||
created=True
|
||||
assert sql(f"SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='{name}'")=='0'
|
||||
record['imageId']=command(['docker','image','inspect','--format','{{.Id}}',IMAGE])
|
||||
record['chromium']=command(['docker','run','--rm','--network','none','--entrypoint','/usr/bin/chromium',IMAGE,'--version'])
|
||||
record['runtime']=command(['docker','run','--rm','--network','none','--entrypoint','dotnet',IMAGE,'--info'])
|
||||
command(['docker','network','create','--label','task=p9-requal-01',network]); network_created=True
|
||||
command(['docker','network','connect','--alias','mariadb',network,DB]); attached=True
|
||||
variables={
|
||||
'ASPNETCORE_ENVIRONMENT':'Development','Database__Provider':'mariadb','Database__ServerVersion':'12.2.2',
|
||||
'ConnectionStrings__JobTracker':f'Server=mariadb;Port=3306;Database={name};User ID=root;Password=jt-integration-only;SslMode=None',
|
||||
'Auth__Require':'true','Auth__JwtKey':'e2e-only-key-at-least-thirty-two-characters-long',
|
||||
'Auth__AdminEmail':'e2e@example.test','Auth__AdminPassword':'E2ePassword123!', 'Auth__AllowRegistration':'false',
|
||||
'RateLimits__AuthLoginPermitLimit':'1000','Cors__Origins__0':'http://localhost:3300',
|
||||
'Workers__AiOperationsEnabled':'false','Workers__SavedJobSearchAlertsEnabled':'false',
|
||||
'HttpsRedirection__Enabled':'false','App__Version':VERSION,'App__CommitSha':'780b9bc',
|
||||
'Data__Root':'/data','Logging__LogLevel__Default':'Warning'}
|
||||
args=['docker','create','--name',api,'--label','task=p9-requal-01','--network',network,
|
||||
'-p','127.0.0.1:5302:8080','--tmpfs','/data:rw,nosuid,size=512m']
|
||||
for key,value in variables.items(): args+=['-e',key+'='+value]
|
||||
command(args+[IMAGE]); api_created=True
|
||||
assert command(['docker','inspect','--format','{{.Image}}',api])==record['imageId']
|
||||
command(['docker','start',api])
|
||||
deadline=time.monotonic()+120
|
||||
while True:
|
||||
try:
|
||||
with urllib.request.urlopen('http://localhost:5302/health',timeout=3) as response:
|
||||
health=json.load(response)
|
||||
assert health['version']==VERSION, health
|
||||
with urllib.request.urlopen('http://localhost:5302/ready',timeout=3) as response:
|
||||
assert json.load(response)['status']=='ready'
|
||||
break
|
||||
except Exception:
|
||||
if time.monotonic()>=deadline: raise
|
||||
time.sleep(1)
|
||||
record['health']=health
|
||||
record['migrationCount']=int(sql(f'SELECT COUNT(*) FROM `{name}`.`__EFMigrationsHistory`'))
|
||||
assert record['migrationCount']>0
|
||||
env={**os.environ,'P9_REQUAL_LINUX_READY':VERSION,'PLAYWRIGHT_STATIC_EXPORT':'true',
|
||||
'PLAYWRIGHT_PREBUILT_EXPORT':'true','NEXT_PUBLIC_API_BASE_URL':'http://localhost:5302/api',
|
||||
'CV_FIDELITY_OUTPUT':str(OUT/'fidelity'),'CV_SETTINGS_FIDELITY_OUTPUT':str(OUT/'settings-fidelity'),
|
||||
'PLAYWRIGHT_JSON_OUTPUT_NAME':str(OUT.parent/'linux-renderer.json')}
|
||||
test=['node','node_modules/@playwright/test/cli.js','test',
|
||||
'--config=../docs/redesign/phase-9/p9-requal-01/linux/playwright.config.ts',
|
||||
'--retries=0','--reporter=list,json','--output='+str(OUT/'test-results')]
|
||||
with (OUT/'playwright.log').open('w',encoding='utf-8') as log:
|
||||
record['browserExitCode']=subprocess.run(['rtk','proxy',*test],cwd=ROOT/'job-tracker-ui',env=env,stdout=log,stderr=subprocess.STDOUT).returncode
|
||||
record['exitCode']=record['browserExitCode']
|
||||
record['migrationCountAfter']=int(sql(f'SELECT COUNT(*) FROM `{name}`.`__EFMigrationsHistory`'))
|
||||
assert record['migrationCountAfter']==record['migrationCount']
|
||||
except Exception as error:
|
||||
record['exitCode']=1
|
||||
record['error']=safe_error(error)
|
||||
finally:
|
||||
if api_created:
|
||||
try:
|
||||
(OUT/'api.log').write_text(safe_error(command(['docker','logs',api])),encoding='utf-8')
|
||||
except Exception as error: record['cleanup']['logError']=safe_error(error)
|
||||
try:
|
||||
command(['docker','rm','--force',api]); record['cleanup']['apiRemoved']=True
|
||||
except Exception as error: record['cleanup']['apiError']=str(error)
|
||||
if attached:
|
||||
try: command(['docker','network','disconnect',network,DB]); record['cleanup']['databaseDisconnected']=True
|
||||
except Exception as error: record['cleanup']['disconnectError']=str(error)
|
||||
if network_created:
|
||||
try: command(['docker','network','rm',network]); record['cleanup']['networkRemoved']=True
|
||||
except Exception as error: record['cleanup']['networkError']=str(error)
|
||||
if created and (not api_created or record['cleanup'].get('apiRemoved')):
|
||||
try:
|
||||
guard_schema(name); sql(f'DROP DATABASE `{name}`')
|
||||
assert sql(f"SELECT COUNT(*) FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='{name}'")=='0'
|
||||
record['cleanup']['schemaRemoved']=True
|
||||
except Exception as error: record['cleanup']['schemaError']=str(error)
|
||||
elif created:
|
||||
record['cleanup']['schemaError']='Schema retained because API removal was not confirmed.'
|
||||
if any(key.endswith('Error') for key in record['cleanup']): record['exitCode']=1
|
||||
record['finishedUtc']=datetime.datetime.now(datetime.timezone.utc).isoformat()
|
||||
(OUT/'process.json').write_text(json.dumps(record,indent=2)+'\n',encoding='utf-8')
|
||||
(OUT.parent/'linux-renderer-exit.json').write_text(json.dumps(record,indent=2)+'\n',encoding='utf-8')
|
||||
print(json.dumps(record))
|
||||
if record['exitCode'] or any(key.endswith('Error') for key in record['cleanup']): raise SystemExit(1)
|
||||
|
||||
if __name__=='__main__': main()
|
||||
Reference in New Issue
Block a user