<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Testing on ErrorZap</title><link>https://errorzap.com/tags/testing/</link><description>Recent content in Testing on ErrorZap</description><image><title>ErrorZap</title><url>https://errorzap.com/og.png</url><link>https://errorzap.com/og.png</link></image><generator>Hugo</generator><language>en-US</language><copyright>ErrorZap</copyright><lastBuildDate>Fri, 02 Oct 2026 23:35:00 -0600</lastBuildDate><atom:link href="https://errorzap.com/tags/testing/index.xml" rel="self" type="application/rss+xml"/><item><title>Tear It Down, Build It Right</title><link>https://errorzap.com/texting/rebuild/</link><pubDate>Fri, 02 Oct 2026 23:35:00 -0600</pubDate><guid>https://errorzap.com/texting/rebuild/</guid><description>How the KCCS Text app went from a web app with instant updates to a full rebuild of the server, the web app and an Android shell in a single afternoon, and what testing against a fake Telnyx caught.</description><content:encoded><![CDATA[<div class="ez-dossier-lede"><span>MY OWN TEXTING · MESSAGE 05 · THE REBUILD</span><p>The app was never the hard part. The hard part was everything the app assumed would just work.</p></div>
<h2 id="step-one-stop-asking-start-listening">Step one: stop asking, start listening</h2>
<p>The old app asked the server &ldquo;anything new?&rdquo; every 10 seconds. The fix is to flip
it around and have the server <strong>tell</strong> the app the moment something arrives.</p>
<p>On September 19 Leo shipped a web app version of the app built that way:</p>
<ul>
<li><strong>Server-Sent Events</strong> keep one quiet connection open to my server, and a new
text shows up on screen the instant it lands. No polling, so no redraws
stomping on my typing.</li>
<li><strong>Web Push</strong> wakes the phone when the app is closed. Leo wrote that part from
the spec and checked it against the official test vectors instead of trusting a
library.</li>
<li><strong>A login per device</strong> that I can revoke one at a time, contact names, sharing
photos into the app, and a Back button that actually goes back.</li>
</ul>
<p>It passed 46 of 46 acceptance checks, and a real text from my main number to my 4040 line
arrived in under six seconds.</p>
<h2 id="never-miss-an-alert">&ldquo;Never miss an alert&rdquo;</h2>
<p>Two days later I&rsquo;d used it enough to know what was still wrong. I wanted different
sounds for different people. I wanted reminders that keep nagging until I answer.
The way I put it to Leo:</p>
<figure class="tx-thread" role="group" aria-label="Sep 21"><div class="tx-thread-head">Sep 21</div>
  <span class="tx-b me"><small>Me → Leo</small>Something I can use all day long and absolutely count on to never miss an alert.</span>
</figure>

<p>A web page can&rsquo;t do that on Android. It can&rsquo;t give each contact its own sound, and
it can&rsquo;t keep reminding you after you swipe it away. A real Android app can. So the
design became a <strong>shell</strong>: a small native Android app that hosts the web app, plus
the native pieces a web page can&rsquo;t do:</p>
<ul>
<li>a background service that holds the live connection to my server,</li>
<li>real Android notifications with <strong>reply right from the notification</strong>,</li>
<li>per-contact sounds, reminders and quiet hours (that&rsquo;s message 06).</li>
</ul>
<h2 id="september-23-one-afternoon">September 23: one afternoon</h2>
<p>On the 23rd I told Leo the app was still buggy. It still deleted what I was typing
sometimes, it jumped around while scrolling, and Back exited the whole app. Leo
stopped patching and rebuilt it.</p>
<p>At 2:34 PM it snapshotted the live code. Then three builder agents worked in
parallel: one on the <strong>server</strong>, one on the <strong>web app</strong>, one on the <strong>Android
shell</strong>, all working to one written contract for how the pieces talk.</p>
<h2 id="a-fake-phone-company-for-testing">A fake phone company for testing</h2>
<p>You can&rsquo;t test a texting app by spamming real texts all afternoon. So the rebuild
came with a <strong>staging harness</strong>. It&rsquo;s my real server code, wired to a <em>fake</em> Telnyx
that never touches the network. Send a text and the fake carrier answers like the
real API, then a second later plays back a &ldquo;delivered&rdquo; receipt through the same
code path real receipts use. It can also inject fake incoming texts and fake
picture messages on command.</p>
<p>Every screenshot of the app in this story comes from that harness, with made-up
people in it.</p>
<h2 id="what-testing-caught">What testing caught</h2>
<p>The server review turned up <strong>18 bugs</strong> in code that had been &ldquo;working.&rdquo; A few
favorites:</p>
<ul>
<li><strong>Vanishing texts.</strong> In a conversation over 500 messages long, the app loaded the
<em>oldest</em> 500. New texts were in the database but never showed up.</li>
<li><strong>Lost-forever texts.</strong> The server marked an incoming webhook as handled
<em>before</em> saving the message. If the save failed, Telnyx would retry, the server
would say &ldquo;already got it,&rdquo; and the text was gone for good.</li>
<li><strong>&ldquo;Retry&rdquo; that didn&rsquo;t.</strong> The retry button never actually re-sent anything.</li>
<li><strong>A freezing server.</strong> A few slow network calls blocked everything else while they
waited.</li>
<li><strong>A sneaky picture.</strong> A picture message pretending to be a certain kind of image
could have run code inside the app with one tap. Now only normal photo and video
types open inline, and everything else downloads as a plain file.</li>
</ul>
<p>Then putting it all together on real phones caught more. My favorite: the app and
the server got into a &ldquo;mark as read&rdquo; argument that fired <strong>647 times in two
minutes</strong>. Another: Android never told the web page it was in the background, so the
page kept marking texts read and <em>cancelling my notifications</em> while the phone
sat in my pocket.</p>
<h2 id="shipped">Shipped</h2>
<p>By the end of the afternoon: 43 server tests, 72 web app tests, 28 of 28 checks
on my Pixel and 27 of 28 automatic checks on my TCL (the last one, replying from
the notification, I checked by hand). At 6:27 PM, version 0.2 went live on both
phones, with a rollback copy saved in case it didn&rsquo;t hold.</p>
<p>It held. So that night, we kept going.</p>
]]></content:encoded></item></channel></rss>