You move an instrument between two accounts. A restriction created for the source account follows the instrument into the destination account. The code finds the right symbol, returns a valid boolean, and still makes the wrong decision.
I would test that ownership boundary before adding another trading rule. A correct symbol match does not establish which account a restriction belongs to.
This post builds a small, copyable JavaScript test for that boundary. It does not connect to a broker, place an order, or reproduce a full trading system. Its claim is narrower: an account-specific restriction must not become a global restriction just because another account holds the same symbol.
Start with the decision, not the database column
The prompt for this example was finaltype's public account-scope write-up. That discussion describes an exclusion applying beyond its intended account. The example below is independent; it is not a patch to that author's application.
Imagine three accounts: source, destination, and unrelated. All three may hold the instrument represented by the invented symbol DEMO.
A user creates a do-not-rebuy flag for source. The first question is not whether a row for DEMO exists. It is whether that row applies to the account making the current decision.
I would write the ownership rule like this:
flag for DEMO
|
+-- account scope: source
| +-- source -> applies
| +-- destination -> does not apply
| +-- unrelated -> does not apply
|
+-- explicit global scope
| +-- every account holding DEMO -> applies
|
+-- missing scope -> unresolved input, not a global default
That is the technical boundary. What an applicable flag actually does is a separate decision. Blocking a future purchase and requesting a sale are not interchangeable actions. This example deliberately produces neither.
Make the three acceptance cases visible
Before the implementation, name the observations that would disprove it.
| Case | Setup | Required observation |
|---|---|---|
| Account isolation | Restriction belongs to source
|
It applies there, but not to destination or unrelated
|
| Explicit global scope | Restriction is deliberately global | It applies across accounts for the matching symbol, but not another symbol |
| Missing ownership | A matching record has no scope, or an account flag has no account ID | Evaluation reports unresolved scope rather than treating it as global |
The unrelated account matters. A test with only the source and destination can become tightly coupled to a particular transfer workflow. A third account checks the broader rule: sharing a symbol is not permission to share an account-specific decision.
The different-symbol assertion matters too. A global restriction in this example is global across accounts, not across every instrument. Those are different meanings of the word global, and a test should force the distinction.
Copy the complete fixture
Save the following as scope.test.mjs. It uses Node's built-in test runner and strict assertions, without dependencies or a broker connection.
import test from 'node:test';
import assert from 'node:assert/strict';
function applies(flag, accountId, symbol) {
if (flag.symbol !== symbol) return false;
if (flag.scope === 'global') return true;
if (flag.scope !== 'account' || !flag.accountId) {
throw new Error('Unresolved flag scope');
}
return flag.accountId === accountId;
}
const sourceFlag = {
symbol: 'DEMO', scope: 'account', accountId: 'source'
};
test('account restriction stays inside its account', () => {
assert.equal(applies(sourceFlag, 'source', 'DEMO'), true);
assert.equal(applies(sourceFlag, 'destination', 'DEMO'), false);
assert.equal(applies(sourceFlag, 'unrelated', 'DEMO'), false);
assert.equal(applies(sourceFlag, 'source', 'OTHER'), false);
});
test('explicit global scope applies to the matching symbol', () => {
const globalFlag = { symbol: 'DEMO', scope: 'global' };
assert.equal(applies(globalFlag, 'source', 'DEMO'), true);
assert.equal(applies(globalFlag, 'destination', 'DEMO'), true);
assert.equal(applies(globalFlag, 'source', 'OTHER'), false);
});
test('missing scope does not silently become global', () => {
assert.throws(() => applies({ symbol: 'DEMO' }, 'source', 'DEMO'),
/Unresolved flag scope/);
assert.throws(() => applies({ symbol: 'DEMO', scope: 'account' },
'source', 'DEMO'), /Unresolved flag scope/);
});
Run it with:
node --test scope.test.mjs
The expected result is three passing tests. The Node test runner documentation explains the command and the node:test module.
For a useful negative control, replace the function body temporarily with return flag.symbol === symbol. The account-isolation case and missing-scope case should fail. If your actual regression suite stays green after removing the ownership check, it is not protecting this boundary.
Missing scope is a migration decision
The example refuses to interpret an old, unscoped record as global. That is a design choice for this fixture, not a universal migration instruction.
An existing product might have promised that every legacy restriction was global. If so, preserving that contract may be correct. Write the migration rule explicitly, label the converted records, and test the intended behavior before replacing the reader.
What I would avoid is letting missing data make that decision accidentally. A nullable column, an empty string, and an explicit global value should not quietly acquire the same meaning without a written rule.
Also keep migration separate from evaluation. A migration can decide how an old record becomes a valid new record. The evaluator can then deal with a much smaller set of supported states. That separation makes the review easier: one change transforms stored meaning; another applies the agreed meaning.
A boolean is only one layer
Passing these tests does not prove that a trading application is safe to run. The fixture assumes validated account and instrument identifiers. It does not resolve broker symbol aliases, verify user permissions, read a database, or test concurrent updates.
In an application, the caller must handle unresolved scope as an explicit error state. Catching the exception and continuing as though no restriction exists would defeat the purpose. A visible hold with a diagnostic reason is one possible policy; the product needs to choose and test its own behavior.
The storage path also needs coverage. If a query discards the account field before this function runs, the function cannot recover the lost information. Test the actual stored record, the query result, and the final decision together in an integration test.
Finally, do not combine this check with broker-balance reconciliation. A restriction leaking across accounts and a ledger disagreeing with broker holdings are separate failures. Give each its own input, expected result, and evidence. Otherwise one passing assertion can hide which behavior was never exercised.
The next useful step is small: find one account-scoped flag in your application, identify the writer and every reader, and add a test using the same symbol in an unrelated account. Keep the expected result beside the test. That makes ownership reviewable before the flag reaches an order-producing path.
Top comments (0)