Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/api-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ jobs:
- name: Run coverage
run: npm run test:frick:cov

# Same mechanics for the staff KYC gate: the files deciding who reaches an elevated endpoint are
# pinned at 100%, so an uncovered branch in the authorization path fails the PR.
- name: Run staff gate coverage
run: npm run test:staff-gate:cov

# Runs on a self-hosted runner for branches of this repository. The gate executes the whole suite
# under full compilation and is CPU-bound; a hosted runner gives a public repo four vCPUs, so Jest
# defaults to three workers and the gate alone decided how long a PR run took. The self-hosted
Expand Down
3 changes: 3 additions & 0 deletions jest.coverage-gate.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,14 @@ const PINNED_LOGIC = [
'src/shared/auth/allow-tfa-pending.decorator.ts',
'src/shared/auth/get-jwt.decorator.ts',
'src/shared/auth/user-role.enum.ts',
'src/shared/decorators/log-rejected-value.decorator.ts',
'src/shared/services/typeorm-logger.ts',
'src/shared/utils/bitbox-ascii.util.ts',
'src/shared/utils/cron.ts',
'src/shared/utils/custom-cron-expression.ts',
'src/shared/utils/request-client.ts',
'src/shared/validators/is-ssrf-safe-url.validator.ts',
'src/shared/validators/xor.validator.ts',
'src/subdomains/core/accounting/controllers/ledger.controller.ts',
'src/subdomains/core/accounting/dto/ledger-account.dto.ts',
'src/subdomains/core/accounting/dto/ledger-dto.mapper.ts',
Expand All @@ -92,6 +94,7 @@ const PINNED_LOGIC = [
'src/subdomains/core/aml/enums/scorechain-outcome.enum.ts',
'src/subdomains/core/aml/services/transaction-aml-check.service.ts',
'src/subdomains/core/buy-crypto/process/exceptions/abort-batch-creation.exception.ts',
'src/subdomains/core/buy-crypto/routes/buy/dto/get-buy-quote.dto.ts',
'src/subdomains/core/buy-crypto/routes/buy/dto/personal-iban-provider.enum.ts',
'src/subdomains/core/custody/dto/output/custody-order-history.dto.ts',
'src/subdomains/core/custody/enums/custody.ts',
Expand Down
32 changes: 32 additions & 0 deletions jest.staff-gate.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Staff KYC gate coverage. Kept out of package.json's shared Jest config (same reasoning as
// jest.frick.config.js) so the strict per-file 100% threshold cannot red an unrelated `test:cov` run —
// only the dedicated test:staff-gate:cov step, with its own --collectCoverageFrom scope, enforces it.
//
// These three files decide who reaches every elevated endpoint. A partially covered branch here is an
// unreviewed hole in the authorization path, so they are pinned at 100% on all four metrics.
const base = require('./package.json').jest;

module.exports = {
...base,
// Coverage instrumentation must match the production build's emit. The main suite runs ts-jest in
// transpile-only mode (isolatedModules), which emits the emitDecoratorMetadata helpers differently
// and produces phantom uncovered branches on dependency-injected constructors. Compile with full
// type info here (tsconfig.coverage.json sets isolatedModules: false) so the 100% gate stays exact.
transform: { '^.+\\.(t|j)s$': ['ts-jest', { tsconfig: 'tsconfig.coverage.json' }] },
coverageThreshold: {
'src/shared/auth/role.guard.ts': { branches: 100, functions: 100, lines: 100, statements: 100 },
'src/shared/auth/staff-kyc-clearance.ts': { branches: 100, functions: 100, lines: 100, statements: 100 },
'src/shared/auth/exceptions/staff-kyc-required.exception.ts': {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
'src/subdomains/generic/user/models/user/staff-kyc-clearance.service.ts': {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"test:cov": "jest --coverage",
"test:frick:cov": "jest --config jest.frick.config.js integration/bank/services/__tests__/frick.service.spec.ts integration/bank/services/__tests__/iso20022.service.spec.ts config/__tests__/frick.config.spec.ts config/__tests__/bank-frick-config.spec.ts subdomains/supporting/bank-tx/bank-tx/services/__tests__/bank-tx-frick.service.spec.ts subdomains/supporting/bank-tx/bank-tx/services/__tests__/bank-tx-outgoing-match.service.spec.ts subdomains/supporting/fiat-output/__tests__/fiat-output-frick.service.spec.ts subdomains/supporting/bank/virtual-iban/__tests__/virtual-iban-frick-issuance-reconciliation.service.spec.ts subdomains/supporting/bank/virtual-iban/__tests__/virtual-iban.service.spec.ts subdomains/supporting/bank/virtual-iban/providers/__tests__/frick-viban.provider.spec.ts --coverage --runInBand --collectCoverageFrom=integration/bank/dto/frick.dto.ts --collectCoverageFrom=integration/bank/services/frick.service.ts --collectCoverageFrom=integration/bank/services/iso20022.service.ts --collectCoverageFrom=config/frick.config.ts --collectCoverageFrom=subdomains/supporting/bank-tx/bank-tx/services/bank-tx-frick.service.ts --collectCoverageFrom=subdomains/supporting/bank-tx/bank-tx/services/bank-tx-outgoing-match.service.ts --collectCoverageFrom=subdomains/supporting/fiat-output/fiat-output-frick.service.ts --collectCoverageFrom=subdomains/supporting/bank/virtual-iban/virtual-iban-frick-issuance-reconciliation.service.ts --collectCoverageFrom=subdomains/supporting/bank/virtual-iban/virtual-iban.service.ts --collectCoverageFrom=subdomains/supporting/bank/virtual-iban/providers/frick-viban.provider.ts",
"test:gate:cov": "jest --config jest.coverage-gate.config.js --coverage --silent",
"test:staff-gate:cov": "jest --config jest.staff-gate.config.js shared/auth/__tests__/role.guard.spec.ts shared/auth/__tests__/staff-kyc-clearance.spec.ts subdomains/generic/user/models/user/__tests__/staff-kyc-clearance.service.spec.ts --coverage --runInBand --collectCoverageFrom=shared/auth/role.guard.ts --collectCoverageFrom=shared/auth/staff-kyc-clearance.ts --collectCoverageFrom=shared/auth/exceptions/staff-kyc-required.exception.ts --collectCoverageFrom=subdomains/generic/user/models/user/staff-kyc-clearance.service.ts",
"type-check": "tsc --noEmit",
"format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
"check": "npm run lint && npm run test",
Expand Down
40 changes: 40 additions & 0 deletions src/integration/exchange/dto/scrypt.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,51 @@ export enum ScryptOrderStatus {
PENDING_REPLACE = 'PendingReplace',
}

/**
* Terminal order statuses at Scrypt: nothing under the reference can still execute.
*
* Single source of truth for both execution-report caching (`OrdStatus`) and order-status lookups
* (`ScryptOrderInfo.status`). Do not re-list these three values elsewhere.
*/
export const SCRYPT_TERMINAL_ORDER_STATUSES: readonly ScryptOrderStatus[] = [
ScryptOrderStatus.FILLED,
ScryptOrderStatus.CANCELED,
ScryptOrderStatus.REJECTED,
];

export function isTerminalScryptOrderStatus(status: ScryptOrderStatus): boolean {
return (SCRYPT_TERMINAL_ORDER_STATUSES as readonly ScryptOrderStatus[]).includes(status);
}

export enum ScryptOrderSide {
BUY = 'Buy',
SELL = 'Sell',
}

/**
* What asking the venue to cancel a reference established about it.
*
* Three outcomes, not two, because "cancelled" does not mean "nothing happened": a partially filled order
* is cancelled with a terminal status AND a fill, and that fill is worth naming rather than folding into
* the same answer as an untouched one.
*/
export enum ScryptCancellation {
/**
* Nothing can execute under this reference any more. Two different qualities of answer: cancelled with
* nothing filled settles it outright, while the venue not knowing the reference is an inference from its
* own words — see SCRYPT_UNKNOWN_ORDER for what that evidence covers.
*/
SETTLED = 'Settled',
/**
* It reached a terminal state with something filled. Like a cancelled reference it cannot trade further,
* so the order may be given up — the fill already moved the venue balance the rule replans from. Kept
* distinct from SETTLED because a fill is worth seeing in a log and worth reconciling against.
*/
EXECUTED = 'Executed',
/** No usable answer. Nothing may be concluded, least of all that the reference is safe to walk away from. */
UNCONFIRMED = 'Unconfirmed',
}

export enum ScryptOrderType {
MARKET = 'Market',
LIMIT = 'Limit',
Expand Down
Loading
Loading