From b3cd739b98e5aff80a4866dfeefdef1082bec0cc Mon Sep 17 00:00:00 2001 From: Eli Mallon Date: Mon, 12 Oct 2026 01:00:09 +0000 Subject: [PATCH] Repair sparse subscription gaps and verify restored upstream sessions --- .maestro/logged-in/upstream-station.yaml | 10 +++++ .maestro/setup/server-setup.yaml | 18 +++++---- go.mod | 2 +- go.sum | 4 +- hack/e2e-local.sh | 8 +++- hack/lib/e2e-harness.sh | 2 +- .../src/components/chat/chat-box.tsx | 5 ++- js/desktop/README.md | 3 ++ js/e2e-web/README.md | 3 ++ .../flows/desktop-upstream-station.spec.ts | 1 + js/e2e-web/flows/upstream-station.spec.ts | 10 +++++ pkg/atproto/firehose.go | 2 +- pkg/atproto/firehose_moq.go | 13 +++---- pkg/atproto/headcheck_test.go | 39 ++++++++++++++++++- pkg/atproto/sweep.go | 19 ++++++++- pkg/cmd/e2e.go | 4 +- 16 files changed, 113 insertions(+), 30 deletions(-) diff --git a/.maestro/logged-in/upstream-station.yaml b/.maestro/logged-in/upstream-station.yaml index 0c13a69b0..e71a88801 100644 --- a/.maestro/logged-in/upstream-station.yaml +++ b/.maestro/logged-in/upstream-station.yaml @@ -22,6 +22,11 @@ tags: - tapOn: id: "chat-input" - inputText: "upstream-station native write" +- extendedWaitUntil: + visible: + text: "Send" + enabled: true + timeout: 30000 - pressKey: Enter - extendedWaitUntil: visible: @@ -46,6 +51,11 @@ tags: - tapOn: id: "chat-input" - inputText: "upstream-station native write after relaunch" +- extendedWaitUntil: + visible: + text: "Send" + enabled: true + timeout: 30000 - pressKey: Enter - extendedWaitUntil: visible: diff --git a/.maestro/setup/server-setup.yaml b/.maestro/setup/server-setup.yaml index 5bc6bd897..eba437e8d 100644 --- a/.maestro/setup/server-setup.yaml +++ b/.maestro/setup/server-setup.yaml @@ -48,12 +48,14 @@ appId: ${APP_ID} - takeScreenshot: server-setup-saved - tapOn: text: "Home" -# The harness's own stream in the feed proves the app is talking to the -# custom node; any card would also match production's feed, which is what -# the app shows until the new node takes effect. Matched as a substring -# because iOS collapses the card into one element with a concatenated label. -- extendedWaitUntil: - visible: - text: ".*e2e test stream.*" - timeout: 30000 +# Standalone nodes already index the fixture stream. Upstream local nodes +# intentionally have no indexed users until the following OAuth login flow. +- runFlow: + when: + true: ${E2E_UPSTREAM_STATION != "true"} + commands: + - extendedWaitUntil: + visible: + text: ".*e2e test stream.*" + timeout: 30000 - takeScreenshot: server-setup-home diff --git a/go.mod b/go.mod index 7bbf62d98..5294f2db7 100644 --- a/go.mod +++ b/go.mod @@ -79,7 +79,7 @@ require ( github.com/streamplace/atproto-oauth-golang v0.0.0-20260908211910-e9bac3356167 github.com/streamplace/glex v0.0.0-20260820164827-814f46540f22 github.com/streamplace/muxl/go v0.3.6 - github.com/streamplace/oatproxy v0.0.0-20261012002509-80a9af303a32 + github.com/streamplace/oatproxy v0.0.0-20261012003324-43c7ac5473f7 github.com/stretchr/testify v1.11.1 github.com/tdewolff/canvas v0.0.0-20250728095813-50d4cb1eee71 github.com/tus/tusd/v2 v2.8.0 diff --git a/go.sum b/go.sum index 33b80c46a..d8e94a12c 100644 --- a/go.sum +++ b/go.sum @@ -1397,8 +1397,8 @@ github.com/streamplace/indigo v0.0.0-20260218231908-939cdaf0c507 h1:e8M3qPLr37Nx github.com/streamplace/indigo v0.0.0-20260218231908-939cdaf0c507/go.mod h1:Pm2I1+iDXn/hLbF7XCg/DsZi6uDCiOo7hZGWprSM7k0= github.com/streamplace/muxl/go v0.3.6 h1:lyWnY15nft/0GK3LBA4OkD2aHrLV9RRqI+YcDRPFk3g= github.com/streamplace/muxl/go v0.3.6/go.mod h1:aCyYTW3o6c1Kush9UJ/Yv6EYMUbj8l8GTD7cHKcSxw8= -github.com/streamplace/oatproxy v0.0.0-20261012002509-80a9af303a32 h1:rZpc/Z3xCx2ooa6ypoUGEPUxifZRBprqmOt5kP0lhJk= -github.com/streamplace/oatproxy v0.0.0-20261012002509-80a9af303a32/go.mod h1:WRKRYf1OcBCa/vWJSJOxabqMIPelwktWcT6WkTj56Hk= +github.com/streamplace/oatproxy v0.0.0-20261012003324-43c7ac5473f7 h1:4cFVSUVeQSX2iAgK4z3PnIqDIZRho/fIwvAbPimvWUU= +github.com/streamplace/oatproxy v0.0.0-20261012003324-43c7ac5473f7/go.mod h1:WRKRYf1OcBCa/vWJSJOxabqMIPelwktWcT6WkTj56Hk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= diff --git a/hack/e2e-local.sh b/hack/e2e-local.sh index 06e743e08..0db11ef2a 100755 --- a/hack/e2e-local.sh +++ b/hack/e2e-local.sh @@ -98,7 +98,10 @@ adb_ shell settings put secure stylus_handwriting_enabled 0 || true # otherwise float over the app and eat maestro's taps adb_ shell settings put global hide_error_dialogs 1 || true -if [ -z "${SERVER_HTTPS_URL:-}" ]; then +if [ "${E2E_UPSTREAM_STATION:-}" = true ]; then + : "${UPSTREAM_STATION_URL:?upstream mode requires the station HTTPS URL}" + : "${PDS_HTTPS_URL:?upstream mode requires the PDS HTTPS URL}" +elif [ -z "${SERVER_HTTPS_URL:-}" ]; then echo "the Android suite needs the harness's HTTPS mode (release builds refuse cleartext); unset E2E_HTTPS_PDS_HOSTNAME" exit 1 fi @@ -143,13 +146,14 @@ adb_ shell pm grant "$APP_ID" android.permission.POST_NOTIFICATIONS 2>/dev/null adb_ shell am force-stop com.android.chrome || true -APP_SERVER_URL="$SERVER_HTTPS_URL" if [ "${E2E_UPSTREAM_STATION:-}" = true ]; then # Keep the issuer and callback loopback names identical on host and device. # Merely substituting 10.0.2.2 would change the DPoP-bound request target. local_port="${SERVER_URL##*:}" adb_ reverse "tcp:$local_port" "tcp:$local_port" APP_SERVER_URL="$SERVER_URL" +else + APP_SERVER_URL="$SERVER_HTTPS_URL" fi if [ "${E2E_UPSTREAM_STATION:-}" = true ]; then e2e_android_stop() { diff --git a/hack/lib/e2e-harness.sh b/hack/lib/e2e-harness.sh index c6811b652..1f4825468 100644 --- a/hack/lib/e2e-harness.sh +++ b/hack/lib/e2e-harness.sh @@ -104,7 +104,7 @@ e2e_harness_start() { export SERVER_HTTPS_URL PDS_HTTPS_URL E2E_PROXY_URL E2E_TLS_SPKI E2E_TLS_CA export E2E_UPSTREAM_STATION UPSTREAM_STATION_URL ATMOQ_RELAY_URL E2E_ATMOQ_LOG export E2E_LOCAL_DATA_DIR E2E_LOCAL_PDS_AUDIT E2E_STATION_DATA_DIR - export UNRELATED_ACCOUNT_DID UNRELATED_ACCOUNT_HANDLE UNRELATED_ACCESS_JWT E2E_PDS_INTERNAL_URL + export UNRELATED_ACCOUNT_DID UNRELATED_ACCOUNT_HANDLE UNRELATED_ACCESS_JWT E2E_PDS_INTERNAL_URL E2E_PLC_URL if [ "${E2E_UPSTREAM_STATION:-}" = true ]; then echo "upstream topology: local=$SERVER_URL station=$UPSTREAM_STATION_URL relay=$ATMOQ_RELAY_URL" fi diff --git a/js/components/src/components/chat/chat-box.tsx b/js/components/src/components/chat/chat-box.tsx index 71ff8cfd4..dc89169b9 100644 --- a/js/components/src/components/chat/chat-box.tsx +++ b/js/components/src/components/chat/chat-box.tsx @@ -114,6 +114,7 @@ export function ChatBox({ const pdsAgent = usePDSAgent(); const userDID = useDID(); + const canSend = !submitting && !!pdsAgent && !!userDID && !!profile; const setActiveTeleportUri = useLivestreamStore( (state) => state.setActiveTeleportUri, ); @@ -329,7 +330,7 @@ export function ChatBox({ }; const submit = async () => { - if (!message.trim()) return; + if (!canSend || !message.trim()) return; if (graphemer.countGraphemes(message) > 300) { toast.show( "Message too long", @@ -580,7 +581,7 @@ export function ChatBox({