From 37f02ca07c4b3447040bd5090c190d057b5c0ae7 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2026 20:24:29 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=AA=20Test=20&=20implement=20executeBa?= =?UTF-8?q?tchPayload=20fallback=20recovery?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added `localStorage` fallback to save `STATE.results` if `executeBatchPayload` fails. - Key is dynamically generated using `telemetry_backup_${STATE.pid}`. - Removed `console.error` and other logging for strict silent client mandate. - Updated DOM status with `textContent` instead of `innerHTML` to prevent XSS. - Created Playwright script (`telemetry_verification/verify_payload_fallback.py`) to verify the offline failure path, UI updates, `localStorage` state, and lack of console errors. Co-authored-by: hashexplaindata <221828969+hashexplaindata@users.noreply.github.com> --- code/experiment.js | 15 +-- .../verify_payload_fallback.py | 96 +++++++++++++++++++ 2 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 telemetry_verification/verify_payload_fallback.py diff --git a/code/experiment.js b/code/experiment.js index 55bce07..b34ed70 100644 --- a/code/experiment.js +++ b/code/experiment.js @@ -349,7 +349,6 @@ function init() { executeBatchPayload(); }); - console.log(`Diagnostic Engine Initialized. PID: ${STATE.pid} | Condition: ${STATE.condition}`); } function loadNextTrial() { @@ -469,13 +468,17 @@ async function executeBatchPayload() { await batch.commit(); onSyncSuccess(); } else { - console.warn("Firebase not detected. Payload logged to console:", STATE.results); - setTimeout(onSyncSuccess, 1500); // Simulate sync delay + // Throw error to trigger the fallback when Firebase is not available + throw new Error("Firebase not initialized"); } } catch (error) { - console.error("Critical Sync Failure:", error); - DOM.syncStatus.innerHTML = `⚠️ Sync Failed. Error: ${error.code || 'Network'}`; - // Potential fallback: Save to localStorage for later recovery + // Fallback: Save to localStorage for later recovery + try { + localStorage.setItem(`telemetry_backup_${STATE.pid}`, JSON.stringify(STATE.results)); + } catch (storageError) { + // Silently fail if localStorage is unavailable + } + DOM.syncStatus.textContent = "Diagnostic Complete. A network timeout occurred. You may safely close this tab."; } } diff --git a/telemetry_verification/verify_payload_fallback.py b/telemetry_verification/verify_payload_fallback.py new file mode 100644 index 0000000..c3d2034 --- /dev/null +++ b/telemetry_verification/verify_payload_fallback.py @@ -0,0 +1,96 @@ +import asyncio +from playwright.async_api import async_playwright +import subprocess +import time +import sys +import json + +async def run_test(): + server = subprocess.Popen(["python3", "-m", "http.server", "8000"]) + # Wait for the server to start + time.sleep(2) + + try: + async with async_playwright() as p: + browser = await p.chromium.launch(headless=True) + context = await browser.new_context() + page = await context.new_page() + + console_messages = [] + + # Listen to console events + page.on("console", lambda msg: console_messages.append(msg.text)) + + print("Navigating to the experiment...") + await page.goto("http://localhost:8000/code/index.html?condition=control") + + print("Starting the trials...") + # Screen 1 -> 2 + await page.click("#btn-consent") + + # Screen 2 -> trial + await page.click(".btn-familiarity[data-val='2']") + + print("Completing trials...") + # 6 Trials + for i in range(6): + await asyncio.sleep(0.5) # Wait for debounce and DOM updates + # Click the first card + await page.click(".bento-choice-card:first-child") + + print("Providing justification...") + # Screen 9 (Justification) + await page.fill("#semantic-justification", "This is a valid semantic justification.") + + print("Simulating offline environment...") + # Set context to offline before clicking finalize to simulate network error for Firebase + await context.set_offline(True) + + print("Finalizing...") + # Click Finalize -> Screen 10 (executeBatchPayload) + await page.click("#btn-finalize") + + await asyncio.sleep(1) # wait for the failure catch block to execute + + print("Verifying text content...") + # Check DOM update + sync_status_text = await page.inner_text("#sync-status") + assert "Diagnostic Complete. A network timeout occurred. You may safely close this tab." in sync_status_text, "Status text does not match expected deception message." + + print("Verifying localStorage backup...") + # Check localStorage + local_storage = await page.evaluate("() => Object.entries(localStorage)") + + backup_key = None + backup_value = None + for key, val in local_storage: + if key.startswith("telemetry_backup_"): + backup_key = key + backup_value = val + break + + assert backup_key is not None, "Backup key not found in localStorage" + + # Verify backup content + data = json.loads(backup_value) + assert len(data) == 6, f"Expected 6 rows in Tidy Data schema, got {len(data)}" + assert "semantic_justification" in data[0], "Semantic justification not appended." + assert data[0]["semantic_justification"] == "This is a valid semantic justification.", "Semantic justification incorrect." + + print("Verifying console silence...") + # Verify console + # Ignore the 404 for firebase-config.js as it's missing from repo + # We are verifying that experiment.js doesn't log errors + errors_or_warnings = [msg for msg in console_messages if ("error" in msg.lower() or "warn" in msg.lower()) and "404" not in msg] + assert len(errors_or_warnings) == 0, f"Found console errors/warnings: {errors_or_warnings}" + + print("All assertions passed. Fallback logic verified successfully.") + + except Exception as e: + print(f"Test Failed: {e}") + sys.exit(1) + finally: + server.terminate() + +if __name__ == "__main__": + asyncio.run(run_test())