DEV Community

Daniel Ioni
Daniel Ioni

Posted on

Stabilizing a Real Node.js Platform: Privacy Fixes, Contract Tests, and Removing False Failures

Stabilizing a Real Node.js Platform: Privacy Fixes, Contract Tests, and Removing False Failures

When a project grows, test failures stop meaning just one thing. Some failures are real production bugs. Some are stale tests. Some come from architectural migrations that left old assumptions behind. Others reveal security or privacy boundaries that were never explicitly tested. And sometimes the worst possible fix is simply changing production code until a brittle assertion turns green.

That is the stage we are currently going through with MyZubster.

The goal is not simply to “make Jest green.” The real goal is to make sure the test suite describes the architecture that actually exists today, while using failures as evidence to find real bugs that would otherwise remain hidden.

MyZubster now contains multiple connected subsystems: authentication and social login, the Zorgax assistant and research layer, Stripe, BTC and MYZ payment flows, marketplace and Seller onboarding, cultural events and artist profiles, community exchange features, accounting and settlement layers, Jest suites, and native node:test suites.

At one point the full Jest run still contained several failing suites. Instead of changing production code mechanically, we started classifying each failure as one of three things: a real production bug, a stale test, or an architectural mismatch. That distinction completely changed how we approached stabilization.

One of the most important examples came from the Cultural API.

An old test expected fields such as organizerId, publicMeetingPoint, approximateArea, and claimedByUserId. However, the current implementation had already evolved toward a different model using ownerId and a structured location object with publicText, restrictedText, mode, and release state.

At first this looked like another obsolete test. It was not.

The public event endpoint was doing this:

CulturalEvent
.findById(req.params.eventId)
.select('-location.restrictedText')
.lean();

That correctly protected the restricted location field, but it still returned ownerId.

The intended rule in the route was clear: public reads must never expose private event coordinates or account ownership.

So a test that initially looked outdated led us to a genuine production privacy issue.

The fix became:

CulturalEvent
.findById(req.params.eventId)
.select('-ownerId -location.restrictedText')
.lean();

We also preserved the existing privacy behavior for hidden locations:

if (
event.location &&
(
event.location.mode === 'PRIVATE' ||
(
event.location.mode === 'AUTHORIZED_RELEASE' &&
!event.location.released
)
)
) {
event.location.publicText = '';
}

The test was then rewritten around the actual privacy contract instead of obsolete field names.

This is exactly why “just update the test” can be dangerous. Sometimes the stale test is still pointing toward something important.

Another group of failures came from UI tests that inspected source code as raw text.

For example, one social login test expected:

data: { providers: providerAvailability() }

while production contained:

data:{providers:providerAvailability()}

Another expected:

localStorage.setItem(
'myzubster-token',
data.data.token
)

while the current React code used:

localStorage.setItem(
'myzubster-token',
d.data.token
)

The behavior was the same. Only the local variable name had changed.

Another test expected:

const RETURN_TO_KEY = 'myzubster-login-return-to'

while the current source was compacted as:

const RETURN_TO_KEY='myzubster-login-return-to';

These are not meaningful regressions.

A source-inspection test should verify semantics where possible, not exact whitespace or arbitrary local variable names. So we replaced fragile string comparisons with semantic regular expressions such as:

expect(page).toMatch(
/const\s+RETURN_TO_KEY\s*=\s*['"]myzubster-login-return-to['"]/
);

After modernizing the assertions, the Social Login and Marketplace Seller UI suites passed without changing production behavior.

The Stripe UI tests exposed another architectural migration.

An old assertion expected the legacy ZorgaxSubscription model to contain the string 'STRIPE'. But that file is now intentionally only a compatibility alias:

module.exports =
require('./ZorgaxPurchase').ZorgaxPurchase;

The payment architecture had moved on.

Paid Zorgax state now belongs to the unified payment domain rather than a separate legacy subscription collection.

So the useful contract is no longer “does this old model contain STRIPE?” The useful contract is whether Zorgax checkout remains dedicated, whether Seller webhook processing distinguishes Zorgax events from Seller events, whether Zorgax invoices are activated correctly, and whether the compatibility alias points to the unified model.

The test was changed accordingly:

expect(route).toContain('createStripeCheckout');

expect(seller).toMatch(
/object.metadata\?.product\s*===\s*['"]zorgax['"]/
);

expect(seller).toMatch(
/activateZorgaxInvoice\s*(\s*object\s*)/
);

expect(subscription).toContain(
"require('./ZorgaxPurchase').ZorgaxPurchase"
);

This verifies the architecture instead of fossilizing an implementation that no longer exists.

After these updates, the three UI suites we were working on reached:
3 suites passed
16 tests passed

Another interesting case came from the community marketplace and Kefir cultures.

The public UI clearly described the feature as free-only. It said things such as “Kefir: solo dono responsabile” and “Solo dono gratuito di colture.” Selecting the Kefir category automatically forced the currency to FREE, and the handover tests also modeled Kefir as a free community transfer.

But the backend still accepted both:

['FREE', 'BARTER']

for Kefir listings.

That meant the user-facing contract and the server contract disagreed.

This time the tests were not wrong. Production needed to change.

The server rule became conceptually:

if (
category === 'kefir_culture_donation' &&
normalizedCurrency !== 'FREE'
) {
return res.status(400).json({
error:
'Le colture di kefir possono essere pubblicate solo come dono gratuito.'
});
}

We also tightened the community exchange classifier so that Kefir counts as a non-commercial community exchange only when it is actually free:

return (
(
category === 'kefir_culture_donation' &&
normalized === 'FREE'
) ||
(
category === 'seeds' &&
['FREE', 'BARTER'].includes(normalized)
)
);

This distinction matters because seeds may still support community barter, while Kefir does not.

We also prevented old non-free Kefir listings from simply being reactivated without first becoming compliant with the free-only rule.

After that change, the Kefir contract suite passed 4 out of 4 tests, while the native Node handover and payment-policy suite passed 8 out of 8.

Another stabilization step involved recognizing that the repository contains two test systems.

Most tests use Jest, but a few lower-level contract tests use the native Node runner through:

require('node:test');

Trying to force every test through Jest created unnecessary conflicts. Instead, the project now treats the two runners explicitly:

{
"scripts": {
"test": "jest",
"test:node": "node --test src/services/marketplaceOrderPaymentPolicy.test.js tests/kefirHandDelivery.node.test.js",
"posttest": "npm run test:node"
}
}

This makes the boundary clear rather than pretending every JavaScript test belongs to the same execution model.

The native suite currently protects important invariants such as server-owned payment amounts, unsupported asset rejection, atomic payment amount validation, free Kefir handovers, donor and recipient confirmation separation, and the rule that free transfers must never imply fake blockchain evidence.

One principle has repeated throughout this work: tests should not force production to lie.

When a test fails, the useful questions are not simply “what string changed?” or “what line do I need to edit?” The questions are: What invariant is this test trying to protect? Does production still implement that invariant? Did the architecture change? Is the test checking behavior or formatting? Would changing production make the system more correct, or would it only satisfy the assertion?

Those questions produced different answers for different failures.

For Cultural Events, production was wrong.

For Social Login, the tests were stale.

For Stripe, the architecture had changed.

For Kefir, the backend contradicted the product contract.

The same red color in Jest represented four completely different engineering problems.

We are also keeping every fix isolated in Git.

When unrelated work exists in the working tree, we avoid broad commands such as:

git add .

Instead, we stage only the files that belong to the specific fix:

git add \
src/routes/listingRoutes.js \
tests/kefirMarketplaceContract.test.js

or:

git add \
src/controllers/zorgaxCulturalController.js \
test/zorgaxCulturalApi.test.js

This matters because a green suite is not worth accidentally committing experimental or unrelated work.

Before the latest UI and Kefir fixes, the full Jest run was at:
Test Suites: 5 failed, 179 passed, 184 total
Tests: 10 failed, 842 passed, 852 total

Since then we have repaired the Cultural privacy contract, the Stripe UI contract, the Social Login UI contract, the Seller UI contract, the Kefir marketplace contract, and the Kefir free-only production rule.

The targeted suites are green.

We are intentionally not claiming that the whole repository is fully green yet, because the final complete run still needs to be executed after the latest fixes.

One known area still being inspected is Zorgax live research source ordering, specifically how Google News and Wikipedia appear when multiple fallback providers return results.

That one will be handled using the same method: first understand the intended contract, then decide whether production or the test should change.

What this work is producing is more valuable than a lower failure count.

We are turning implicit behavior into explicit architecture.

Public Cultural APIs must not expose account ownership.

Restricted locations must remain private.

Kefir transfers are free-only community exchanges.

Seller activation and Zorgax payments are separate domains.

Legacy models must not become parallel sources of truth.

Payment expectations should come from server-owned state.

Source-inspection tests should assert semantics instead of formatting.

Native Node tests and Jest can coexist without pretending to be one runner.

These rules are gradually becoming part of the project's technical knowledge, not just code that happens to work today.

The next step is to carry the same knowledge into the MyZubster Knowledge site, where these lessons can become canonical documentation for architecture, privacy, payments, testing, community exchange rules, and operational decisions.

The core lesson from this stabilization work is simple:

Tests should preserve architecture, not fossilize old implementations.

And when a failing test reveals a real privacy or product-contract bug, it should be treated as valuable evidence rather than noise.

nodejs #testing #javascript #opensource

Top comments (0)