fix: scale replicas in response, K8s metrics client, quota precheck, auth tests

- Add GetMetrics method to MetricsClient interface and implement cluster metrics API
- Add QuotaPrecheck service for validating resource quotas before deployment
- Add auth DTO with role/permission models and auth handler tests
- Add instance diagnostics: mounted NFS volumes, labels, annotations in pod diagnostics
- Update workspace handler with GetWorkspace endpoint and shared-user list
- Fix monitoring handler to use correct service method name
- Add tail_lines fallback in instance handler for snake_case query params
- Update nginx config for SSE log streaming support (no buffering)
- Add comprehensive test coverage: auth_service_test, auth_handler_test,
  auth_dto_test, metrics_client_test, quota_precheck_test
- Update error messages for quota validation and instance operations
- ModifyModal: fix YAML lineWidth:0, modified keys summary, delta-only submit
- InstanceCard: correctly disable scale-minus when replicas <= 0
- SidebarLayout: add hover transition for sidebar items
- Update todo.md and lessons.md with latest fixes
This commit is contained in:
Ivan087
2026-05-20 16:56:29 +08:00
parent 8f90cf0f0d
commit 33ddaf97db
59 changed files with 4805 additions and 457 deletions

View File

@ -1,7 +1,8 @@
#!/usr/bin/env python3
# Covers InstanceCard action layout: creates a harmless failed metadata instance
# with an invalid chart before Helm runs, opens the Instances page, verifies the
# Delete button remains inside the card and viewport, clicks it, and cleans up.
# Covers InstanceCard action layout. It prefers a harmless failed metadata
# instance when the API preserves one; if chart validation rejects before DB
# persistence, it falls back to mocking only the instance list/delete API so the
# visual overflow assertion remains independent from deployment behavior.
import json
import os
@ -102,6 +103,7 @@ def main() -> int:
release = f"ocdp-ui-overflow-{suffix}"
namespace = f"ocdp-ui-overflow-{suffix}"
instance_id = ""
synthetic = False
try:
create = request(
"POST",
@ -125,16 +127,44 @@ def main() -> int:
break
time.sleep(0.5)
if not instance_id:
raise AssertionError("test instance was not visible after failed chart download")
synthetic = True
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page(viewport={"width": 920, "height": 760})
page.on("dialog", lambda dialog: dialog.accept())
login_ui(page)
if synthetic:
synthetic_instance = {
"id": f"synthetic-{suffix}",
"name": release,
"namespace": namespace,
"clusterId": cluster_id,
"registryId": registry_id,
"repository": "charts/nonexistent",
"chart": "nonexistent",
"version": "0.0.0",
"status": "failed",
"ownerUsername": ADMIN_USER,
"values": {},
"createdAt": "2026-05-15T00:00:00Z",
"updatedAt": "2026-05-15T00:00:00Z",
}
def fulfill_instances(route):
if route.request.method == "GET":
route.fulfill(status=200, content_type="application/json", body=json.dumps({"instances": [synthetic_instance], "total": 1}))
return
if route.request.method == "DELETE":
route.fulfill(status=204, body="")
return
route.continue_()
page.route("**/api/v1/clusters/*/instances", fulfill_instances)
page.route("**/api/v1/clusters/*/instances/*", fulfill_instances)
page.get_by_role("button", name="Instances", exact=True).click()
page.wait_for_load_state("networkidle")
heading = page.get_by_role("heading", name=release, exact=True)
heading = page.get_by_role("heading", name=release, exact=True).first
expect(heading).to_be_visible(timeout=15000)
card = heading.locator("xpath=ancestor::div[contains(@class, 'group')][1]")
delete_button = card.get_by_role("button", name="Delete", exact=True)