mirror of
https://github.com/selfxyz/self.git
synced 2026-04-27 03:01:15 -04:00
fix: OFAC trees not found (#1060)
* fix: relax OFAC tree response validation * test: cover OFAC tree edge cases * fix stateless * revert and fix types * fix tests
This commit is contained in:
@@ -18,6 +18,7 @@ import {
|
||||
} from '@selfxyz/common/utils';
|
||||
import { getPublicKey, verifyAttestation } from '@selfxyz/common/utils/attest';
|
||||
import {
|
||||
generateTEEInputsDiscloseStateless,
|
||||
generateTEEInputsDSC,
|
||||
generateTEEInputsRegister,
|
||||
} from '@selfxyz/common/utils/circuits/registerInputs';
|
||||
@@ -38,7 +39,6 @@ import {
|
||||
} from '@selfxyz/common/utils/proving';
|
||||
import {
|
||||
clearPassportData,
|
||||
generateTEEInputsDisclose,
|
||||
hasAnyValidRegisteredDocument,
|
||||
loadSelectedDocument,
|
||||
markCurrentDocumentAsRegistered,
|
||||
@@ -459,7 +459,7 @@ export const useProvingStore = create<ProvingState>((set, get) => {
|
||||
selfClient.emit(SdkEvents.PROVING_REGISTER_ERROR_OR_FAILURE, {
|
||||
hasValidDocument: hasValid,
|
||||
});
|
||||
} catch (error) {
|
||||
} catch {
|
||||
selfClient.emit(SdkEvents.PROVING_REGISTER_ERROR_OR_FAILURE, {
|
||||
hasValidDocument: false,
|
||||
});
|
||||
@@ -1015,7 +1015,7 @@ export const useProvingStore = create<ProvingState>((set, get) => {
|
||||
}
|
||||
},
|
||||
|
||||
_closeConnections: (selfClient: SelfClient) => {
|
||||
_closeConnections: (_selfClient: SelfClient) => {
|
||||
const { wsConnection: ws, wsHandlers } = get();
|
||||
if (ws && wsHandlers) {
|
||||
try {
|
||||
@@ -1088,10 +1088,27 @@ export const useProvingStore = create<ProvingState>((set, get) => {
|
||||
break;
|
||||
case 'disclose':
|
||||
({ inputs, circuitName, endpointType, endpoint } =
|
||||
generateTEEInputsDisclose(
|
||||
generateTEEInputsDiscloseStateless(
|
||||
secret as string,
|
||||
passportData,
|
||||
selfApp as SelfApp,
|
||||
(doc: DocumentCategory, tree) => {
|
||||
const docStore =
|
||||
doc === 'passport'
|
||||
? protocolStore.passport
|
||||
: protocolStore.id_card;
|
||||
switch (tree) {
|
||||
case 'ofac':
|
||||
return docStore.ofac_trees;
|
||||
case 'commitment':
|
||||
if (!docStore.commitment_tree) {
|
||||
throw new Error('Commitment tree not loaded');
|
||||
}
|
||||
return docStore.commitment_tree;
|
||||
default:
|
||||
throw new Error('Unknown tree type');
|
||||
}
|
||||
},
|
||||
));
|
||||
circuitTypeWithDocumentExtension = `disclose`;
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,342 @@
|
||||
// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import type { SelfClient } from '@selfxyz/mobile-sdk-alpha';
|
||||
import {
|
||||
useProtocolStore,
|
||||
useSelfAppStore,
|
||||
} from '@selfxyz/mobile-sdk-alpha/stores';
|
||||
|
||||
// Do not import provingMachine here; we'll require it after setting up mocks per test
|
||||
|
||||
jest.mock('xstate', () => {
|
||||
const actual = jest.requireActual('xstate') as any;
|
||||
const { actorMock } = require('./actorMock');
|
||||
return { ...actual, createActor: jest.fn(() => actorMock) };
|
||||
});
|
||||
|
||||
// Mock proving utils for payload building
|
||||
jest.mock('@selfxyz/common/utils/proving', () => {
|
||||
const actual = jest.requireActual('@selfxyz/common/utils/proving') as any;
|
||||
return {
|
||||
...actual,
|
||||
getPayload: jest.fn(() => ({ mocked: true })),
|
||||
encryptAES256GCM: jest.fn(() => ({
|
||||
nonce: [0],
|
||||
cipher_text: [1],
|
||||
auth_tag: [2],
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
||||
describe('_generatePayload disclose (stateless resolver)', () => {
|
||||
const selfClient: SelfClient = {
|
||||
trackEvent: jest.fn(),
|
||||
} as unknown as SelfClient;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.clearAllMocks();
|
||||
|
||||
useSelfAppStore.setState({
|
||||
selfApp: {
|
||||
chainID: 42220,
|
||||
userId: '12345678-1234-1234-1234-123456789abc',
|
||||
userDefinedData: '0x0',
|
||||
endpointType: 'https',
|
||||
endpoint: 'https://endpoint',
|
||||
scope: 'scope',
|
||||
sessionId: '',
|
||||
appName: '',
|
||||
logoBase64: '',
|
||||
header: '',
|
||||
userIdType: 'uuid',
|
||||
devMode: false,
|
||||
disclosures: {},
|
||||
version: 1,
|
||||
deeplinkCallback: '',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('uses resolver to fetch ofac and commitment trees', async () => {
|
||||
// Mock the stateless generator to assert resolver behavior
|
||||
const genMock = jest.fn((secret, passportData, selfApp, getTree) => {
|
||||
const ofac = getTree('passport', 'ofac');
|
||||
const commit = getTree('passport', 'commitment');
|
||||
expect(ofac).toEqual({
|
||||
passportNoAndNationality: { root: ['pp'] },
|
||||
nameAndDob: { root: ['dob'] },
|
||||
nameAndYob: { root: ['yob'] },
|
||||
});
|
||||
expect(commit).toBe('[[]]');
|
||||
return {
|
||||
inputs: { s: 1 },
|
||||
circuitName: 'vc_and_disclose',
|
||||
endpointType: 'https',
|
||||
endpoint: 'https://dis',
|
||||
};
|
||||
});
|
||||
jest.doMock('@selfxyz/common/utils/circuits/registerInputs', () => ({
|
||||
generateTEEInputsDiscloseStateless: genMock,
|
||||
generateTEEInputsRegister: jest.fn(),
|
||||
generateTEEInputsDSC: jest.fn(),
|
||||
}));
|
||||
|
||||
// Act (reload module after doMock)
|
||||
let store: any;
|
||||
let protocolStore: any;
|
||||
jest.isolateModules(() => {
|
||||
// require after mocks are in place
|
||||
const mod = require('@/utils/proving/provingMachine');
|
||||
const {
|
||||
useProtocolStore: isolatedProtocolStore,
|
||||
} = require('@selfxyz/mobile-sdk-alpha/stores');
|
||||
store = mod.useProvingStore;
|
||||
protocolStore = isolatedProtocolStore;
|
||||
|
||||
// Set protocol store state inside isolateModules
|
||||
protocolStore.setState({
|
||||
passport: {
|
||||
dsc_tree: 'tree',
|
||||
csca_tree: [[new Uint8Array([1])]],
|
||||
commitment_tree: '[[]]',
|
||||
deployed_circuits: null,
|
||||
circuits_dns_mapping: null,
|
||||
alternative_csca: {},
|
||||
ofac_trees: {
|
||||
passportNoAndNationality: { root: ['pp'] },
|
||||
nameAndDob: { root: ['dob'] },
|
||||
nameAndYob: { root: ['yob'] },
|
||||
},
|
||||
fetch_deployed_circuits: jest.fn(),
|
||||
fetch_circuits_dns_mapping: jest.fn(),
|
||||
fetch_csca_tree: jest.fn(),
|
||||
fetch_dsc_tree: jest.fn(),
|
||||
fetch_identity_tree: jest.fn(),
|
||||
fetch_alternative_csca: jest.fn(),
|
||||
fetch_ofac_trees: jest.fn(),
|
||||
fetch_all: jest.fn(),
|
||||
},
|
||||
id_card: {
|
||||
commitment_tree: null,
|
||||
dsc_tree: null,
|
||||
csca_tree: null,
|
||||
deployed_circuits: null,
|
||||
circuits_dns_mapping: null,
|
||||
alternative_csca: {},
|
||||
ofac_trees: null,
|
||||
fetch_deployed_circuits: jest.fn(),
|
||||
fetch_circuits_dns_mapping: jest.fn(),
|
||||
fetch_csca_tree: jest.fn(),
|
||||
fetch_dsc_tree: jest.fn(),
|
||||
fetch_identity_tree: jest.fn(),
|
||||
fetch_alternative_csca: jest.fn(),
|
||||
fetch_ofac_trees: jest.fn(),
|
||||
fetch_all: jest.fn(),
|
||||
},
|
||||
} as any);
|
||||
|
||||
// Set proving store state inside isolateModules so it affects the isolated store instance
|
||||
store.setState({
|
||||
circuitType: 'disclose',
|
||||
passportData: {
|
||||
documentCategory: 'passport',
|
||||
mock: false,
|
||||
dsc_parsed: { authorityKeyIdentifier: 'abcd' },
|
||||
passportMetadata: {
|
||||
signatureAlgorithm: 'rsa_pss_rsae_sha256',
|
||||
signedAttrHashFunction: 'sha256',
|
||||
issuer: 'X',
|
||||
validFrom: new Date('2020-01-01'),
|
||||
validTo: new Date('2030-01-01'),
|
||||
},
|
||||
mrz: 'P<UTO...MOCKMRZ...',
|
||||
eContent: [],
|
||||
signedAttr: [],
|
||||
encryptedDigest: [],
|
||||
} as any,
|
||||
secret: 'sec',
|
||||
uuid: 'uuid-123',
|
||||
sharedKey: Buffer.alloc(32, 1),
|
||||
env: 'prod',
|
||||
});
|
||||
});
|
||||
const payload = await store.getState()._generatePayload(selfClient);
|
||||
|
||||
// Assert
|
||||
expect(genMock).toHaveBeenCalled();
|
||||
expect(store.getState().endpointType).toBe('https');
|
||||
expect(payload.params).toEqual({
|
||||
uuid: 'uuid-123',
|
||||
nonce: [0],
|
||||
cipher_text: [1],
|
||||
auth_tag: [2],
|
||||
});
|
||||
});
|
||||
|
||||
it('throws when commitment tree is missing', async () => {
|
||||
const genMock = jest.fn((secret, passportData, selfApp, getTree) => {
|
||||
// This should throw inside resolver when requesting commitment
|
||||
getTree('passport', 'commitment');
|
||||
return {
|
||||
inputs: {},
|
||||
circuitName: '',
|
||||
endpointType: 'https',
|
||||
endpoint: '',
|
||||
};
|
||||
});
|
||||
jest.doMock('@selfxyz/common/utils/circuits/registerInputs', () => ({
|
||||
generateTEEInputsDiscloseStateless: genMock,
|
||||
generateTEEInputsRegister: jest.fn(),
|
||||
generateTEEInputsDSC: jest.fn(),
|
||||
}));
|
||||
|
||||
let store: any;
|
||||
let protocolStore: any;
|
||||
jest.isolateModules(() => {
|
||||
const mod = require('@/utils/proving/provingMachine');
|
||||
const {
|
||||
useProtocolStore: isolatedProtocolStore,
|
||||
} = require('@selfxyz/mobile-sdk-alpha/stores');
|
||||
store = mod.useProvingStore;
|
||||
protocolStore = isolatedProtocolStore;
|
||||
|
||||
// Set protocol store state inside isolateModules - missing commitment tree
|
||||
protocolStore.setState({
|
||||
passport: {
|
||||
dsc_tree: 'tree',
|
||||
csca_tree: [[new Uint8Array([1])]],
|
||||
commitment_tree: null,
|
||||
deployed_circuits: null,
|
||||
circuits_dns_mapping: null,
|
||||
alternative_csca: {},
|
||||
ofac_trees: {
|
||||
passportNoAndNationality: { root: ['pp'] },
|
||||
nameAndDob: { root: ['dob'] },
|
||||
nameAndYob: { root: ['yob'] },
|
||||
},
|
||||
fetch_deployed_circuits: jest.fn(),
|
||||
fetch_circuits_dns_mapping: jest.fn(),
|
||||
fetch_csca_tree: jest.fn(),
|
||||
fetch_dsc_tree: jest.fn(),
|
||||
fetch_identity_tree: jest.fn(),
|
||||
fetch_alternative_csca: jest.fn(),
|
||||
fetch_ofac_trees: jest.fn(),
|
||||
fetch_all: jest.fn(),
|
||||
},
|
||||
id_card: {} as any,
|
||||
} as any);
|
||||
|
||||
// Set store state inside isolateModules so it affects the isolated store instance
|
||||
store.setState({
|
||||
circuitType: 'disclose',
|
||||
passportData: {
|
||||
documentCategory: 'passport',
|
||||
mock: false,
|
||||
dsc_parsed: { authorityKeyIdentifier: 'abcd' },
|
||||
passportMetadata: {
|
||||
signatureAlgorithm: 'rsa_pss_rsae_sha256',
|
||||
signedAttrHashFunction: 'sha256',
|
||||
issuer: 'X',
|
||||
validFrom: new Date('2020-01-01'),
|
||||
validTo: new Date('2030-01-01'),
|
||||
},
|
||||
mrz: 'P<UTO...MOCKMRZ...',
|
||||
eContent: [],
|
||||
signedAttr: [],
|
||||
encryptedDigest: [],
|
||||
} as any,
|
||||
secret: 'sec',
|
||||
uuid: 'uuid-123',
|
||||
sharedKey: Buffer.alloc(32, 1),
|
||||
env: 'prod',
|
||||
});
|
||||
});
|
||||
await expect(store.getState()._generatePayload(selfClient)).rejects.toThrow(
|
||||
'Commitment tree not loaded',
|
||||
);
|
||||
});
|
||||
|
||||
it('throws when OFAC trees are missing', async () => {
|
||||
const genMock = jest.fn((secret, passportData, selfApp, getTree) => {
|
||||
const ofac = getTree('passport', 'ofac');
|
||||
if (!ofac) {
|
||||
throw new Error('OFAC trees not loaded');
|
||||
}
|
||||
return {
|
||||
inputs: {},
|
||||
circuitName: '',
|
||||
endpointType: 'https',
|
||||
endpoint: '',
|
||||
};
|
||||
});
|
||||
jest.doMock('@selfxyz/common/utils/circuits/registerInputs', () => ({
|
||||
generateTEEInputsDiscloseStateless: genMock,
|
||||
generateTEEInputsRegister: jest.fn(),
|
||||
generateTEEInputsDSC: jest.fn(),
|
||||
}));
|
||||
|
||||
let store: any;
|
||||
let protocolStore: any;
|
||||
jest.isolateModules(() => {
|
||||
const mod = require('@/utils/proving/provingMachine');
|
||||
const {
|
||||
useProtocolStore: isolatedProtocolStore,
|
||||
} = require('@selfxyz/mobile-sdk-alpha/stores');
|
||||
store = mod.useProvingStore;
|
||||
protocolStore = isolatedProtocolStore;
|
||||
|
||||
// Set protocol store state inside isolateModules - missing OFAC trees
|
||||
protocolStore.setState({
|
||||
passport: {
|
||||
dsc_tree: 'tree',
|
||||
csca_tree: [[new Uint8Array([1])]],
|
||||
commitment_tree: '[[]]',
|
||||
deployed_circuits: null,
|
||||
circuits_dns_mapping: null,
|
||||
alternative_csca: {},
|
||||
ofac_trees: null,
|
||||
fetch_deployed_circuits: jest.fn(),
|
||||
fetch_circuits_dns_mapping: jest.fn(),
|
||||
fetch_csca_tree: jest.fn(),
|
||||
fetch_dsc_tree: jest.fn(),
|
||||
fetch_identity_tree: jest.fn(),
|
||||
fetch_alternative_csca: jest.fn(),
|
||||
fetch_ofac_trees: jest.fn(),
|
||||
fetch_all: jest.fn(),
|
||||
},
|
||||
id_card: {} as any,
|
||||
} as any);
|
||||
|
||||
// Set store state inside isolateModules so it affects the isolated store instance
|
||||
store.setState({
|
||||
circuitType: 'disclose',
|
||||
passportData: {
|
||||
documentCategory: 'passport',
|
||||
mock: false,
|
||||
dsc_parsed: { authorityKeyIdentifier: 'abcd' },
|
||||
passportMetadata: {
|
||||
signatureAlgorithm: 'rsa_pss_rsae_sha256',
|
||||
signedAttrHashFunction: 'sha256',
|
||||
issuer: 'X',
|
||||
validFrom: new Date('2020-01-01'),
|
||||
validTo: new Date('2030-01-01'),
|
||||
},
|
||||
mrz: 'P<UTO...MOCKMRZ...',
|
||||
eContent: [],
|
||||
signedAttr: [],
|
||||
encryptedDigest: [],
|
||||
} as any,
|
||||
secret: 'sec',
|
||||
uuid: 'uuid-123',
|
||||
sharedKey: Buffer.alloc(32, 1),
|
||||
env: 'prod',
|
||||
});
|
||||
});
|
||||
await expect(store.getState()._generatePayload(selfClient)).rejects.toThrow(
|
||||
'OFAC trees not loaded',
|
||||
);
|
||||
});
|
||||
});
|
||||
73
common/src/utils/ofac.test.ts
Normal file
73
common/src/utils/ofac.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { fetchOfacTrees } from './ofac';
|
||||
|
||||
const originalFetch = global.fetch;
|
||||
|
||||
describe('fetchOfacTrees', () => {
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
global.fetch = originalFetch;
|
||||
});
|
||||
|
||||
it('accepts raw tree payloads', async () => {
|
||||
const responses: Record<string, any> = {
|
||||
'passport-no-nationality': { root: ['pp'] },
|
||||
'name-dob': { root: ['dob'] },
|
||||
'name-yob': { root: ['yob'] },
|
||||
};
|
||||
|
||||
vi.spyOn(global, 'fetch').mockImplementation(
|
||||
(input: string | Request | URL, _init?: RequestInit) => {
|
||||
const url = typeof input === 'string' ? input : input.toString();
|
||||
const key = url.includes('passport-no-nationality')
|
||||
? 'passport-no-nationality'
|
||||
: url.includes('name-dob')
|
||||
? 'name-dob'
|
||||
: 'name-yob';
|
||||
return Promise.resolve({ ok: true, json: async () => responses[key] } as Response);
|
||||
}
|
||||
);
|
||||
|
||||
const trees = await fetchOfacTrees('prod', 'passport');
|
||||
expect(trees).toEqual({
|
||||
passportNoAndNationality: responses['passport-no-nationality'],
|
||||
nameAndDob: responses['name-dob'],
|
||||
nameAndYob: responses['name-yob'],
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts wrapped {status, data} payloads', async () => {
|
||||
const responses: Record<string, any> = {
|
||||
'passport-no-nationality': { status: 'success', data: { root: ['pp'] } },
|
||||
'name-dob': { status: 'success', data: { root: ['dob'] } },
|
||||
'name-yob': { status: 'success', data: { root: ['yob'] } },
|
||||
};
|
||||
|
||||
vi.spyOn(global, 'fetch').mockImplementation(
|
||||
(input: string | Request | URL, _init?: RequestInit) => {
|
||||
const url = typeof input === 'string' ? input : input.toString();
|
||||
const key = url.includes('passport-no-nationality')
|
||||
? 'passport-no-nationality'
|
||||
: url.includes('name-dob')
|
||||
? 'name-dob'
|
||||
: 'name-yob';
|
||||
return Promise.resolve({ ok: true, json: async () => responses[key] } as Response);
|
||||
}
|
||||
);
|
||||
|
||||
const trees = await fetchOfacTrees('prod', 'passport');
|
||||
expect(trees).toEqual({
|
||||
passportNoAndNationality: responses['passport-no-nationality'].data,
|
||||
nameAndDob: responses['name-dob'].data,
|
||||
nameAndYob: responses['name-yob'].data,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -14,12 +14,19 @@ const fetchTree = async (url: string): Promise<any> => {
|
||||
throw new Error(`HTTP error fetching ${url}! status: ${res.status}`);
|
||||
}
|
||||
const responseData = await res.json();
|
||||
if (responseData.status !== 'success' || !responseData.data) {
|
||||
throw new Error(
|
||||
`Failed to fetch tree from ${url}: ${responseData.message || 'Invalid response format'}`
|
||||
);
|
||||
|
||||
// Handle wrapped responses with {status: 'success', data: ...} format
|
||||
if (responseData && typeof responseData === 'object' && 'status' in responseData) {
|
||||
if (responseData.status !== 'success' || !responseData.data) {
|
||||
throw new Error(
|
||||
`Failed to fetch tree from ${url}: ${responseData.message || 'Invalid response format'}`
|
||||
);
|
||||
}
|
||||
return responseData.data;
|
||||
}
|
||||
return responseData.data;
|
||||
|
||||
// Handle raw responses (direct tree data)
|
||||
return responseData;
|
||||
};
|
||||
|
||||
// Main public helper that retrieves the three OFAC trees depending on the variant (passport vs id_card).
|
||||
|
||||
@@ -145,4 +145,17 @@ describe('generateTEEInputsDisclose', () => {
|
||||
`Invalid OFAC tree structure: missing required fields`,
|
||||
);
|
||||
});
|
||||
|
||||
it('throws error if OFAC trees not loaded', () => {
|
||||
vi.spyOn(useProtocolStore, 'getState').mockReturnValue({
|
||||
passport: {
|
||||
ofac_trees: null,
|
||||
commitment_tree: '[[]]',
|
||||
},
|
||||
} as any);
|
||||
|
||||
expect(() => generateTEEInputsDisclose(mockSecret, mockPassportData, mockSelfApp)).toThrowError(
|
||||
'OFAC trees not loaded',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
76
packages/mobile-sdk-alpha/tests/stores/protocolStore.test.ts
Normal file
76
packages/mobile-sdk-alpha/tests/stores/protocolStore.test.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
/**
|
||||
* @vitest-environment node
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { useProtocolStore } from '../../src/stores/protocolStore';
|
||||
|
||||
const originalFetch = global.fetch;
|
||||
|
||||
describe('protocolStore.fetch_ofac_trees', () => {
|
||||
beforeEach(() => {
|
||||
useProtocolStore.setState(state => ({
|
||||
passport: { ...state.passport, ofac_trees: null },
|
||||
}));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
global.fetch = originalFetch;
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('stores OFAC trees when responses are raw payloads', async () => {
|
||||
const responses: Record<string, any> = {
|
||||
'passport-no-nationality': { root: ['pp'] },
|
||||
'name-dob': { root: ['dob'] },
|
||||
'name-yob': { root: ['yob'] },
|
||||
};
|
||||
|
||||
vi.spyOn(global, 'fetch').mockImplementation((input: string | Request | URL, _init?: RequestInit) => {
|
||||
const url = typeof input === 'string' ? input : input.toString();
|
||||
const key = url.includes('passport-no-nationality')
|
||||
? 'passport-no-nationality'
|
||||
: url.includes('name-dob')
|
||||
? 'name-dob'
|
||||
: 'name-yob';
|
||||
return Promise.resolve({ ok: true, json: async () => responses[key] } as Response);
|
||||
});
|
||||
|
||||
await useProtocolStore.getState().passport.fetch_ofac_trees('prod');
|
||||
expect(useProtocolStore.getState().passport.ofac_trees).toEqual({
|
||||
passportNoAndNationality: responses['passport-no-nationality'],
|
||||
nameAndDob: responses['name-dob'],
|
||||
nameAndYob: responses['name-yob'],
|
||||
});
|
||||
});
|
||||
|
||||
it('stores OFAC trees when responses are wrapped payloads', async () => {
|
||||
const responses: Record<string, any> = {
|
||||
'passport-no-nationality': { status: 'success', data: { root: ['pp'] } },
|
||||
'name-dob': { status: 'success', data: { root: ['dob'] } },
|
||||
'name-yob': { status: 'success', data: { root: ['yob'] } },
|
||||
};
|
||||
|
||||
vi.spyOn(global, 'fetch').mockImplementation((input: string | Request | URL, _init?: RequestInit) => {
|
||||
const url = typeof input === 'string' ? input : input.toString();
|
||||
const key = url.includes('passport-no-nationality')
|
||||
? 'passport-no-nationality'
|
||||
: url.includes('name-dob')
|
||||
? 'name-dob'
|
||||
: 'name-yob';
|
||||
return Promise.resolve({ ok: true, json: async () => responses[key] } as Response);
|
||||
});
|
||||
|
||||
await useProtocolStore.getState().passport.fetch_ofac_trees('prod');
|
||||
expect(useProtocolStore.getState().passport.ofac_trees).toEqual({
|
||||
passportNoAndNationality: responses['passport-no-nationality'].data,
|
||||
nameAndDob: responses['name-dob'].data,
|
||||
nameAndYob: responses['name-yob'].data,
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user