mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 07:41:27 +00:00
fix: e2e QA hardening — honest runtime state, stable public ports, read-only anon UI (#12)
CI / Infra (Compose + Scripts) (push) Canceled after 0s
CI / Backend (Go) (push) Canceled after 0s
CI / Frontend (React) (push) Canceled after 0s
CI / Compose boot smoke (push) Canceled after 0s
CI / Build & publish images (push) Canceled after 0s
CI / Release (push) Canceled after 0s
CI / Infra (Compose + Scripts) (push) Canceled after 0s
CI / Backend (Go) (push) Canceled after 0s
CI / Frontend (React) (push) Canceled after 0s
CI / Compose boot smoke (push) Canceled after 0s
CI / Build & publish images (push) Canceled after 0s
CI / Release (push) Canceled after 0s
* fix: e2e QA hardening — honest runtime URLs, health, and read-only anon UI Backend: - managed DB provisioning: attach DB containers to the platform network and probe the container IP, fixing the 127.0.0.1 reachability failure inside the backend container; log provisioning failures - stop fabricating connection URLs when provisioning produced none - template deploys now carry port + health_check into service runtime fields - service health probed over the project network (backend joins it) instead of an in-container wget that fails on shell-less images - runtime/public/connection URLs rewrite loopback to the request Host so links are usable for LAN/remote clients Frontend: - service Networking panel shows real runtime URLs/ports/health instead of hardcoded containr.dev + 8080->443 - anonymous views hide all mutation controls (new project, add service, group, context menu) - project cards: remove decorative svcN labels; badge says DOWN only when zero services run, IDLE when empty, DEGRADED for partial - mobile projects header no longer clips (fluid search width) - unknown routes render a not-found page instead of silently redirecting Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix: telemetry scope label, chunk split, remove stale vite artifacts - /system/host now reports scope=host|docker-desktop-vm and the daemon OS; Projects dashboard labels cards VM.* when the daemon is a Desktop VM so memory/core numbers aren't mistaken for physical host values - manualChunks: split @xyflow/react and recharts out of the main bundle; removes the >1MB chunk warning - drop tracked vite.config.js/.d.ts build artifacts that shadowed the real vite.config.ts Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> * fix: persist service host port; demo-data scope for telemetry Public URLs were ephemeral: every restart/redeploy rebound a random host port, so endpoint links rotted. Persist published_port on the service and prefer it for replica 0 on create. If the port is taken, fall back to ephemeral and repersist the new assignment. Post-start the binding is verified — Docker Desktop accepts a conflicting publish silently, which previously left services running with no endpoint at all. demo-data.ts gains the required scope field for HostMonitoring (CI fix). Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
d968eb2315
commit
32b52d121f
23 files changed
+541
-205
No files matched your search
@@ -51,15 +51,16 @@ func TestDatabaseConnectionURLAndDefaultVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
url := handler.generateConnectionURL(DatabaseService{
|
||||
Type: tc.dbType,
|
||||
Name: "example",
|
||||
})
|
||||
if url == "" {
|
||||
t.Fatalf("generateConnectionURL returned empty for type %q", tc.dbType)
|
||||
plan, err := buildDatabaseRuntimePlan(tc.dbType, "example", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("buildDatabaseRuntimePlan(%q) failed: %v", tc.dbType, err)
|
||||
}
|
||||
url := plan.ConnectionURL("15432")
|
||||
if len(tc.urlContains) > 0 && !strings.HasPrefix(url, tc.urlContains) {
|
||||
t.Fatalf("generateConnectionURL(%q) = %q, expected prefix %q", tc.dbType, url, tc.urlContains)
|
||||
t.Fatalf("ConnectionURL(%q) = %q, expected prefix %q", tc.dbType, url, tc.urlContains)
|
||||
}
|
||||
if !strings.Contains(url, ":15432") {
|
||||
t.Fatalf("ConnectionURL(%q) = %q, expected published port", tc.dbType, url)
|
||||
}
|
||||
|
||||
gotVersion := handler.getDefaultVersion(tc.dbType)
|
||||
@@ -69,6 +70,17 @@ func TestDatabaseConnectionURLAndDefaultVersion(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveConnectionURLNeverFabricates(t *testing.T) {
|
||||
handler := &DatabaseHandler{}
|
||||
|
||||
if got := handler.resolveConnectionURL(DatabaseService{Type: "postgresql", Name: "example"}); got != "" {
|
||||
t.Fatalf("resolveConnectionURL with no stored URL = %q, want empty", got)
|
||||
}
|
||||
if got := handler.resolveConnectionURL(DatabaseService{ConnectionURL: "postgresql://u:p@localhost:15432/db"}); got != "postgresql://u:p@localhost:15432/db" {
|
||||
t.Fatalf("resolveConnectionURL = %q, want stored URL", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDatabaseRuntimePlanSupportsAllTypes(t *testing.T) {
|
||||
cases := []struct {
|
||||
dbType string
|
||||
@@ -199,3 +211,23 @@ func TestHumanReadableBytes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRewriteLoopbackURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name, raw, host, want string
|
||||
}{
|
||||
{"service url", "http://localhost:35981", "10.0.0.182", "http://10.0.0.182:35981"},
|
||||
{"db url keeps creds", "postgresql://u:p@localhost:35432/db?sslmode=disable", "10.0.0.182", "postgresql://u:[email protected]:35432/db?sslmode=disable"},
|
||||
{"ipv4 loopback", "redis://:[email protected]:36379/0", "host.lan", "redis://:[email protected]:36379/0"},
|
||||
{"unspecified", "http://0.0.0.0:9000/x", "10.0.0.182", "http://10.0.0.182:9000/x"},
|
||||
{"external host untouched", "https://example.com:443/x", "10.0.0.182", "https://example.com:443/x"},
|
||||
{"empty raw", "", "10.0.0.182", ""},
|
||||
{"empty host", "http://localhost:35981", "", "http://localhost:35981"},
|
||||
{"no port", "http://localhost/path", "10.0.0.182", "http://10.0.0.182/path"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
if got := rewriteLoopbackURL(tt.raw, tt.host); got != tt.want {
|
||||
t.Errorf("%s: rewriteLoopbackURL(%q,%q) = %q, want %q", tt.name, tt.raw, tt.host, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user