@fintekkers/ledger-models 0.4.10 → 0.4.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/node/wrappers/models/hydrate.test.d.ts +1 -0
- package/node/wrappers/models/hydrate.test.js +204 -0
- package/node/wrappers/models/hydrate.test.js.map +1 -0
- package/node/wrappers/models/hydrate.test.ts +214 -0
- package/node/wrappers/models/lazy-hydrate-race.test.d.ts +1 -0
- package/node/wrappers/models/lazy-hydrate-race.test.js +153 -0
- package/node/wrappers/models/lazy-hydrate-race.test.js.map +1 -0
- package/node/wrappers/models/lazy-hydrate-race.test.ts +144 -0
- package/node/wrappers/models/lazy-hydrate.bench.test.d.ts +1 -0
- package/node/wrappers/models/lazy-hydrate.bench.test.js +121 -0
- package/node/wrappers/models/lazy-hydrate.bench.test.js.map +1 -0
- package/node/wrappers/models/lazy-hydrate.bench.test.ts +103 -0
- package/node/wrappers/models/portfolio/portfolio.d.ts +12 -2
- package/node/wrappers/models/portfolio/portfolio.js +42 -3
- package/node/wrappers/models/portfolio/portfolio.js.map +1 -1
- package/node/wrappers/models/portfolio/portfolio.ts +27 -3
- package/node/wrappers/models/security/identifier-validation.test.d.ts +1 -0
- package/node/wrappers/models/security/identifier-validation.test.js +104 -0
- package/node/wrappers/models/security/identifier-validation.test.js.map +1 -0
- package/node/wrappers/models/security/identifier-validation.test.ts +133 -0
- package/node/wrappers/models/security/identifier.d.ts +26 -0
- package/node/wrappers/models/security/identifier.js +54 -1
- package/node/wrappers/models/security/identifier.js.map +1 -1
- package/node/wrappers/models/security/identifier.test.js +6 -9
- package/node/wrappers/models/security/identifier.test.js.map +1 -1
- package/node/wrappers/models/security/identifier.test.ts +6 -9
- package/node/wrappers/models/security/identifier.ts +58 -0
- package/node/wrappers/models/security/security.d.ts +23 -5
- package/node/wrappers/models/security/security.js +53 -6
- package/node/wrappers/models/security/security.js.map +1 -1
- package/node/wrappers/models/security/security.ts +38 -6
- package/node/wrappers/models/transaction/transaction.d.ts +12 -1
- package/node/wrappers/models/transaction/transaction.js +39 -2
- package/node/wrappers/models/transaction/transaction.js.map +1 -1
- package/node/wrappers/models/transaction/transaction.ts +27 -2
- package/node/wrappers/services/security-service/SecurityService.js +8 -0
- package/node/wrappers/services/security-service/SecurityService.js.map +1 -1
- package/node/wrappers/services/security-service/SecurityService.ts +10 -0
- package/node/wrappers/services/security.identifier-guard.test.d.ts +1 -0
- package/node/wrappers/services/security.identifier-guard.test.js +63 -0
- package/node/wrappers/services/security.identifier-guard.test.js.map +1 -0
- package/node/wrappers/services/security.identifier-guard.test.ts +70 -0
- package/node/wrappers/util/link-cache.d.ts +13 -6
- package/node/wrappers/util/link-cache.js +51 -15
- package/node/wrappers/util/link-cache.js.map +1 -1
- package/node/wrappers/util/link-cache.ts +51 -17
- package/node/wrappers/util/link-resolver.d.ts +39 -31
- package/node/wrappers/util/link-resolver.js +132 -124
- package/node/wrappers/util/link-resolver.js.map +1 -1
- package/node/wrappers/util/link-resolver.test.js +13 -2
- package/node/wrappers/util/link-resolver.test.js.map +1 -1
- package/node/wrappers/util/link-resolver.test.ts +14 -2
- package/node/wrappers/util/link-resolver.ts +141 -151
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Tests for the W1 async `hydrate()` method on Security/Portfolio wrappers
|
|
3
|
+
// and the LinkResolver default singleton.
|
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
+
if (k2 === undefined) k2 = k;
|
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(o, k2, desc);
|
|
11
|
+
}) : (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
o[k2] = m[k];
|
|
14
|
+
}));
|
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
+
}) : function(o, v) {
|
|
18
|
+
o["default"] = v;
|
|
19
|
+
});
|
|
20
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
21
|
+
if (mod && mod.__esModule) return mod;
|
|
22
|
+
var result = {};
|
|
23
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
24
|
+
__setModuleDefault(result, mod);
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
28
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
29
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
30
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
31
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
32
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
33
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const uuid_1 = require("./utils/uuid");
|
|
41
|
+
const datetime_1 = require("./utils/datetime");
|
|
42
|
+
const local_timestamp_pb_1 = require("../../fintekkers/models/util/local_timestamp_pb");
|
|
43
|
+
const timestamp_pb_1 = require("google-protobuf/google/protobuf/timestamp_pb");
|
|
44
|
+
const security_pb_1 = require("../../fintekkers/models/security/security_pb");
|
|
45
|
+
const portfolio_pb_1 = require("../../fintekkers/models/portfolio/portfolio_pb");
|
|
46
|
+
const query_security_response_pb_1 = require("../../fintekkers/requests/security/query_security_response_pb");
|
|
47
|
+
const query_portfolio_response_pb_1 = require("../../fintekkers/requests/portfolio/query_portfolio_response_pb");
|
|
48
|
+
const security_1 = __importDefault(require("./security/security"));
|
|
49
|
+
const portfolio_1 = __importDefault(require("./portfolio/portfolio"));
|
|
50
|
+
const link_resolver_1 = __importDefault(require("../util/link-resolver"));
|
|
51
|
+
const LinkCacheModule = __importStar(require("../util/link-cache"));
|
|
52
|
+
function makeAsOf(seconds = 1700000000) {
|
|
53
|
+
const ts = new timestamp_pb_1.Timestamp();
|
|
54
|
+
ts.setSeconds(seconds);
|
|
55
|
+
ts.setNanos(0);
|
|
56
|
+
const proto = new local_timestamp_pb_1.LocalTimestampProto();
|
|
57
|
+
proto.setTimestamp(ts);
|
|
58
|
+
proto.setTimeZone('UTC');
|
|
59
|
+
return proto;
|
|
60
|
+
}
|
|
61
|
+
function fullSecurityProto(uuid, asOf, issuer) {
|
|
62
|
+
const p = new security_pb_1.SecurityProto();
|
|
63
|
+
p.setUuid(uuid.toUUIDProto());
|
|
64
|
+
p.setAsOf(asOf);
|
|
65
|
+
p.setIssuerName(issuer);
|
|
66
|
+
return p;
|
|
67
|
+
}
|
|
68
|
+
function fullPortfolioProto(uuid, asOf, name) {
|
|
69
|
+
const p = new portfolio_pb_1.PortfolioProto();
|
|
70
|
+
p.setUuid(uuid.toUUIDProto());
|
|
71
|
+
p.setAsOf(asOf);
|
|
72
|
+
p.setPortfolioName(name);
|
|
73
|
+
return p;
|
|
74
|
+
}
|
|
75
|
+
// Stub clients that return canned protos for GetByIds. The
|
|
76
|
+
// `LinkResolverOptions.{security,portfolio}Client` slots expect the full
|
|
77
|
+
// generated client class; we only implement the one method LinkResolver
|
|
78
|
+
// uses and assert the shape to the public client type at the injection
|
|
79
|
+
// site — keeps the test typed end-to-end without re-stubbing dozens of
|
|
80
|
+
// grpc.Client methods.
|
|
81
|
+
function stubSecurityClient(canned) {
|
|
82
|
+
const stub = {
|
|
83
|
+
getByIds(_req, cb) {
|
|
84
|
+
const resp = new query_security_response_pb_1.QuerySecurityResponseProto();
|
|
85
|
+
resp.setSecurityResponseList([canned]);
|
|
86
|
+
cb(null, resp);
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
return stub;
|
|
90
|
+
}
|
|
91
|
+
function stubPortfolioClient(canned) {
|
|
92
|
+
const stub = {
|
|
93
|
+
getByIds(_req, cb) {
|
|
94
|
+
const resp = new query_portfolio_response_pb_1.QueryPortfolioResponseProto();
|
|
95
|
+
resp.setPortfolioResponseList([canned]);
|
|
96
|
+
cb(null, resp);
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
return stub;
|
|
100
|
+
}
|
|
101
|
+
/** Stand-in for the "other" client slot in `LinkResolverOptions` when a
|
|
102
|
+
* test only exercises one entity type — never invoked, but the option is
|
|
103
|
+
* required by the constructor. */
|
|
104
|
+
function unusedSecurityClient() {
|
|
105
|
+
return {
|
|
106
|
+
getByIds() {
|
|
107
|
+
throw new Error('unused stub: security client should not be called');
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function unusedPortfolioClient() {
|
|
112
|
+
return {
|
|
113
|
+
getByIds() {
|
|
114
|
+
throw new Error('unused stub: portfolio client should not be called');
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
beforeEach(() => {
|
|
119
|
+
LinkCacheModule.SECURITY.clear();
|
|
120
|
+
LinkCacheModule.PORTFOLIO.clear();
|
|
121
|
+
link_resolver_1.default.setDefault(undefined);
|
|
122
|
+
});
|
|
123
|
+
afterEach(() => {
|
|
124
|
+
link_resolver_1.default.setDefault(undefined);
|
|
125
|
+
});
|
|
126
|
+
// ---- LinkResolver default singleton ----
|
|
127
|
+
test('LinkResolver.getDefault returns a stable singleton until cleared', () => {
|
|
128
|
+
const a = link_resolver_1.default.getDefault();
|
|
129
|
+
const b = link_resolver_1.default.getDefault();
|
|
130
|
+
expect(a).toBe(b);
|
|
131
|
+
link_resolver_1.default.setDefault(undefined);
|
|
132
|
+
const c = link_resolver_1.default.getDefault();
|
|
133
|
+
expect(c).not.toBe(a);
|
|
134
|
+
});
|
|
135
|
+
test('LinkResolver.setDefault overrides the singleton', () => {
|
|
136
|
+
const custom = new link_resolver_1.default();
|
|
137
|
+
link_resolver_1.default.setDefault(custom);
|
|
138
|
+
expect(link_resolver_1.default.getDefault()).toBe(custom);
|
|
139
|
+
});
|
|
140
|
+
// ---- Security.hydrate ----
|
|
141
|
+
test('Security.hydrate() on a non-link wrapper is a no-op', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
142
|
+
const uuid = uuid_1.UUID.random();
|
|
143
|
+
const asOf = makeAsOf(1);
|
|
144
|
+
const sec = new security_1.default(fullSecurityProto(uuid, asOf, 'NOT-LINKED'));
|
|
145
|
+
// No resolver registered — would throw if hydrate tried to fetch.
|
|
146
|
+
yield sec.hydrate();
|
|
147
|
+
expect(sec.getIssuerName()).toBe('NOT-LINKED');
|
|
148
|
+
}));
|
|
149
|
+
test('Security.hydrate() fetches via the default resolver and swaps the proto', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
150
|
+
const uuid = uuid_1.UUID.random();
|
|
151
|
+
const asOf = makeAsOf(2);
|
|
152
|
+
const resolved = fullSecurityProto(uuid, asOf, 'FROM-RESOLVER');
|
|
153
|
+
const customResolver = new link_resolver_1.default({
|
|
154
|
+
securityClient: stubSecurityClient(resolved),
|
|
155
|
+
portfolioClient: unusedPortfolioClient(),
|
|
156
|
+
});
|
|
157
|
+
link_resolver_1.default.setDefault(customResolver);
|
|
158
|
+
const sec = new security_1.default(security_1.default.linkOf(uuid, new datetime_1.ZonedDateTime(asOf)));
|
|
159
|
+
expect(sec.isLink()).toBe(true);
|
|
160
|
+
yield sec.hydrate();
|
|
161
|
+
expect(sec.getIssuerName()).toBe('FROM-RESOLVER');
|
|
162
|
+
// After hydrate, accessor reads are sync — the proto has been swapped.
|
|
163
|
+
expect(sec.proto.getIsLink()).toBe(false);
|
|
164
|
+
}));
|
|
165
|
+
test('Security.hydrate() accepts an explicit resolver instead of the default', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
166
|
+
const uuid = uuid_1.UUID.random();
|
|
167
|
+
const asOf = makeAsOf(3);
|
|
168
|
+
const resolved = fullSecurityProto(uuid, asOf, 'EXPLICIT');
|
|
169
|
+
const explicit = new link_resolver_1.default({
|
|
170
|
+
securityClient: stubSecurityClient(resolved),
|
|
171
|
+
portfolioClient: unusedPortfolioClient(),
|
|
172
|
+
});
|
|
173
|
+
const sec = new security_1.default(security_1.default.linkOf(uuid, new datetime_1.ZonedDateTime(asOf)));
|
|
174
|
+
yield sec.hydrate(explicit);
|
|
175
|
+
expect(sec.getIssuerName()).toBe('EXPLICIT');
|
|
176
|
+
}));
|
|
177
|
+
// ---- Portfolio.hydrate ----
|
|
178
|
+
test('Portfolio.hydrate() fetches via the default resolver and swaps the proto', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
179
|
+
const uuid = uuid_1.UUID.random();
|
|
180
|
+
const asOf = makeAsOf(4);
|
|
181
|
+
const resolved = fullPortfolioProto(uuid, asOf, 'Strategy Z');
|
|
182
|
+
const customResolver = new link_resolver_1.default({
|
|
183
|
+
securityClient: unusedSecurityClient(),
|
|
184
|
+
portfolioClient: stubPortfolioClient(resolved),
|
|
185
|
+
});
|
|
186
|
+
link_resolver_1.default.setDefault(customResolver);
|
|
187
|
+
// Build a link Portfolio (no static helper on Portfolio; construct manually).
|
|
188
|
+
const linkProto = new portfolio_pb_1.PortfolioProto();
|
|
189
|
+
linkProto.setUuid(uuid.toUUIDProto());
|
|
190
|
+
linkProto.setAsOf(asOf);
|
|
191
|
+
linkProto.setIsLink(true);
|
|
192
|
+
const p = new portfolio_1.default(linkProto);
|
|
193
|
+
expect(p.isLink()).toBe(true);
|
|
194
|
+
yield p.hydrate();
|
|
195
|
+
expect(p.getPortfolioName()).toBe('Strategy Z');
|
|
196
|
+
}));
|
|
197
|
+
test('Portfolio.hydrate() on a non-link wrapper is a no-op', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
198
|
+
const uuid = uuid_1.UUID.random();
|
|
199
|
+
const asOf = makeAsOf(5);
|
|
200
|
+
const p = new portfolio_1.default(fullPortfolioProto(uuid, asOf, 'Already Full'));
|
|
201
|
+
yield p.hydrate();
|
|
202
|
+
expect(p.getPortfolioName()).toBe('Already Full');
|
|
203
|
+
}));
|
|
204
|
+
//# sourceMappingURL=hydrate.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hydrate.test.js","sourceRoot":"","sources":["hydrate.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// Tests for the W1 async `hydrate()` method on Security/Portfolio wrappers
|
|
2
|
+
// and the LinkResolver default singleton.
|
|
3
|
+
|
|
4
|
+
import * as grpc from '@grpc/grpc-js';
|
|
5
|
+
|
|
6
|
+
import { UUID } from './utils/uuid';
|
|
7
|
+
import { ZonedDateTime } from './utils/datetime';
|
|
8
|
+
import { LocalTimestampProto } from '../../fintekkers/models/util/local_timestamp_pb';
|
|
9
|
+
import { Timestamp } from 'google-protobuf/google/protobuf/timestamp_pb';
|
|
10
|
+
import { SecurityProto } from '../../fintekkers/models/security/security_pb';
|
|
11
|
+
import { PortfolioProto } from '../../fintekkers/models/portfolio/portfolio_pb';
|
|
12
|
+
import { QuerySecurityRequestProto } from '../../fintekkers/requests/security/query_security_request_pb';
|
|
13
|
+
import { QuerySecurityResponseProto } from '../../fintekkers/requests/security/query_security_response_pb';
|
|
14
|
+
import { QueryPortfolioRequestProto } from '../../fintekkers/requests/portfolio/query_portfolio_request_pb';
|
|
15
|
+
import { QueryPortfolioResponseProto } from '../../fintekkers/requests/portfolio/query_portfolio_response_pb';
|
|
16
|
+
import { SecurityClient } from '../../fintekkers/services/security-service/security_service_grpc_pb';
|
|
17
|
+
import { PortfolioClient } from '../../fintekkers/services/portfolio-service/portfolio_service_grpc_pb';
|
|
18
|
+
|
|
19
|
+
import Security from './security/security';
|
|
20
|
+
import Portfolio from './portfolio/portfolio';
|
|
21
|
+
import LinkResolver from '../util/link-resolver';
|
|
22
|
+
import * as LinkCacheModule from '../util/link-cache';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Minimal stub shape — captures the one method `LinkResolver` calls.
|
|
26
|
+
* Cast to `SecurityClient` / `PortfolioClient` at the injection site so
|
|
27
|
+
* the test reads `securityClient: stub(...)` without an `as any` escape.
|
|
28
|
+
*/
|
|
29
|
+
type GetByIdsCallback<TRes> = (
|
|
30
|
+
err: grpc.ServiceError | null,
|
|
31
|
+
res: TRes,
|
|
32
|
+
) => void;
|
|
33
|
+
|
|
34
|
+
interface SecurityGetByIdsOnly {
|
|
35
|
+
getByIds(req: QuerySecurityRequestProto, cb: GetByIdsCallback<QuerySecurityResponseProto>): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface PortfolioGetByIdsOnly {
|
|
39
|
+
getByIds(req: QueryPortfolioRequestProto, cb: GetByIdsCallback<QueryPortfolioResponseProto>): void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function makeAsOf(seconds = 1_700_000_000): LocalTimestampProto {
|
|
43
|
+
const ts = new Timestamp();
|
|
44
|
+
ts.setSeconds(seconds);
|
|
45
|
+
ts.setNanos(0);
|
|
46
|
+
const proto = new LocalTimestampProto();
|
|
47
|
+
proto.setTimestamp(ts);
|
|
48
|
+
proto.setTimeZone('UTC');
|
|
49
|
+
return proto;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function fullSecurityProto(uuid: UUID, asOf: LocalTimestampProto, issuer: string): SecurityProto {
|
|
53
|
+
const p = new SecurityProto();
|
|
54
|
+
p.setUuid(uuid.toUUIDProto());
|
|
55
|
+
p.setAsOf(asOf);
|
|
56
|
+
p.setIssuerName(issuer);
|
|
57
|
+
return p;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function fullPortfolioProto(uuid: UUID, asOf: LocalTimestampProto, name: string): PortfolioProto {
|
|
61
|
+
const p = new PortfolioProto();
|
|
62
|
+
p.setUuid(uuid.toUUIDProto());
|
|
63
|
+
p.setAsOf(asOf);
|
|
64
|
+
p.setPortfolioName(name);
|
|
65
|
+
return p;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Stub clients that return canned protos for GetByIds. The
|
|
69
|
+
// `LinkResolverOptions.{security,portfolio}Client` slots expect the full
|
|
70
|
+
// generated client class; we only implement the one method LinkResolver
|
|
71
|
+
// uses and assert the shape to the public client type at the injection
|
|
72
|
+
// site — keeps the test typed end-to-end without re-stubbing dozens of
|
|
73
|
+
// grpc.Client methods.
|
|
74
|
+
function stubSecurityClient(canned: SecurityProto): SecurityClient {
|
|
75
|
+
const stub: SecurityGetByIdsOnly = {
|
|
76
|
+
getByIds(_req, cb) {
|
|
77
|
+
const resp = new QuerySecurityResponseProto();
|
|
78
|
+
resp.setSecurityResponseList([canned]);
|
|
79
|
+
cb(null, resp);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
return stub as unknown as SecurityClient;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function stubPortfolioClient(canned: PortfolioProto): PortfolioClient {
|
|
86
|
+
const stub: PortfolioGetByIdsOnly = {
|
|
87
|
+
getByIds(_req, cb) {
|
|
88
|
+
const resp = new QueryPortfolioResponseProto();
|
|
89
|
+
resp.setPortfolioResponseList([canned]);
|
|
90
|
+
cb(null, resp);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
return stub as unknown as PortfolioClient;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Stand-in for the "other" client slot in `LinkResolverOptions` when a
|
|
97
|
+
* test only exercises one entity type — never invoked, but the option is
|
|
98
|
+
* required by the constructor. */
|
|
99
|
+
function unusedSecurityClient(): SecurityClient {
|
|
100
|
+
return {
|
|
101
|
+
getByIds(): never {
|
|
102
|
+
throw new Error('unused stub: security client should not be called');
|
|
103
|
+
},
|
|
104
|
+
} as unknown as SecurityClient;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function unusedPortfolioClient(): PortfolioClient {
|
|
108
|
+
return {
|
|
109
|
+
getByIds(): never {
|
|
110
|
+
throw new Error('unused stub: portfolio client should not be called');
|
|
111
|
+
},
|
|
112
|
+
} as unknown as PortfolioClient;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
beforeEach(() => {
|
|
116
|
+
LinkCacheModule.SECURITY.clear();
|
|
117
|
+
LinkCacheModule.PORTFOLIO.clear();
|
|
118
|
+
LinkResolver.setDefault(undefined);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
afterEach(() => {
|
|
122
|
+
LinkResolver.setDefault(undefined);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// ---- LinkResolver default singleton ----
|
|
126
|
+
|
|
127
|
+
test('LinkResolver.getDefault returns a stable singleton until cleared', () => {
|
|
128
|
+
const a = LinkResolver.getDefault();
|
|
129
|
+
const b = LinkResolver.getDefault();
|
|
130
|
+
expect(a).toBe(b);
|
|
131
|
+
LinkResolver.setDefault(undefined);
|
|
132
|
+
const c = LinkResolver.getDefault();
|
|
133
|
+
expect(c).not.toBe(a);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('LinkResolver.setDefault overrides the singleton', () => {
|
|
137
|
+
const custom = new LinkResolver();
|
|
138
|
+
LinkResolver.setDefault(custom);
|
|
139
|
+
expect(LinkResolver.getDefault()).toBe(custom);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// ---- Security.hydrate ----
|
|
143
|
+
|
|
144
|
+
test('Security.hydrate() on a non-link wrapper is a no-op', async () => {
|
|
145
|
+
const uuid = UUID.random();
|
|
146
|
+
const asOf = makeAsOf(1);
|
|
147
|
+
const sec = new Security(fullSecurityProto(uuid, asOf, 'NOT-LINKED'));
|
|
148
|
+
// No resolver registered — would throw if hydrate tried to fetch.
|
|
149
|
+
await sec.hydrate();
|
|
150
|
+
expect(sec.getIssuerName()).toBe('NOT-LINKED');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('Security.hydrate() fetches via the default resolver and swaps the proto', async () => {
|
|
154
|
+
const uuid = UUID.random();
|
|
155
|
+
const asOf = makeAsOf(2);
|
|
156
|
+
const resolved = fullSecurityProto(uuid, asOf, 'FROM-RESOLVER');
|
|
157
|
+
const customResolver = new LinkResolver({
|
|
158
|
+
securityClient: stubSecurityClient(resolved),
|
|
159
|
+
portfolioClient: unusedPortfolioClient(),
|
|
160
|
+
});
|
|
161
|
+
LinkResolver.setDefault(customResolver);
|
|
162
|
+
|
|
163
|
+
const sec = new Security(Security.linkOf(uuid, new ZonedDateTime(asOf)));
|
|
164
|
+
expect(sec.isLink()).toBe(true);
|
|
165
|
+
await sec.hydrate();
|
|
166
|
+
expect(sec.getIssuerName()).toBe('FROM-RESOLVER');
|
|
167
|
+
// After hydrate, accessor reads are sync — the proto has been swapped.
|
|
168
|
+
expect(sec.proto.getIsLink()).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('Security.hydrate() accepts an explicit resolver instead of the default', async () => {
|
|
172
|
+
const uuid = UUID.random();
|
|
173
|
+
const asOf = makeAsOf(3);
|
|
174
|
+
const resolved = fullSecurityProto(uuid, asOf, 'EXPLICIT');
|
|
175
|
+
const explicit = new LinkResolver({
|
|
176
|
+
securityClient: stubSecurityClient(resolved),
|
|
177
|
+
portfolioClient: unusedPortfolioClient(),
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const sec = new Security(Security.linkOf(uuid, new ZonedDateTime(asOf)));
|
|
181
|
+
await sec.hydrate(explicit);
|
|
182
|
+
expect(sec.getIssuerName()).toBe('EXPLICIT');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// ---- Portfolio.hydrate ----
|
|
186
|
+
|
|
187
|
+
test('Portfolio.hydrate() fetches via the default resolver and swaps the proto', async () => {
|
|
188
|
+
const uuid = UUID.random();
|
|
189
|
+
const asOf = makeAsOf(4);
|
|
190
|
+
const resolved = fullPortfolioProto(uuid, asOf, 'Strategy Z');
|
|
191
|
+
const customResolver = new LinkResolver({
|
|
192
|
+
securityClient: unusedSecurityClient(),
|
|
193
|
+
portfolioClient: stubPortfolioClient(resolved),
|
|
194
|
+
});
|
|
195
|
+
LinkResolver.setDefault(customResolver);
|
|
196
|
+
|
|
197
|
+
// Build a link Portfolio (no static helper on Portfolio; construct manually).
|
|
198
|
+
const linkProto = new PortfolioProto();
|
|
199
|
+
linkProto.setUuid(uuid.toUUIDProto());
|
|
200
|
+
linkProto.setAsOf(asOf);
|
|
201
|
+
linkProto.setIsLink(true);
|
|
202
|
+
const p = new Portfolio(linkProto);
|
|
203
|
+
expect(p.isLink()).toBe(true);
|
|
204
|
+
await p.hydrate();
|
|
205
|
+
expect(p.getPortfolioName()).toBe('Strategy Z');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test('Portfolio.hydrate() on a non-link wrapper is a no-op', async () => {
|
|
209
|
+
const uuid = UUID.random();
|
|
210
|
+
const asOf = makeAsOf(5);
|
|
211
|
+
const p = new Portfolio(fullPortfolioProto(uuid, asOf, 'Already Full'));
|
|
212
|
+
await p.hydrate();
|
|
213
|
+
expect(p.getPortfolioName()).toBe('Already Full');
|
|
214
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Concurrent-hydrate race test for Portfolio + Transaction wrappers.
|
|
3
|
+
// TypeScript has no shared-state threads, but Promise.all + a mock LinkResolver
|
|
4
|
+
// covers the same shape: N parallel `await wrapper.hydrate()` calls on
|
|
5
|
+
// wrappers that share a UUID. Contract:
|
|
6
|
+
//
|
|
7
|
+
// 1. Resolver's in-flight dedup collapses N hydrate() calls into one RPC.
|
|
8
|
+
// 2. Every awaiter sees the resolved proto.
|
|
9
|
+
// 3. The shared LinkCache singleton ends on the resolved entry.
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
27
|
+
if (mod && mod.__esModule) return mod;
|
|
28
|
+
var result = {};
|
|
29
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
30
|
+
__setModuleDefault(result, mod);
|
|
31
|
+
return result;
|
|
32
|
+
};
|
|
33
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
34
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
35
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
36
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
37
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
38
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
39
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
43
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
const link_resolver_1 = __importDefault(require("../util/link-resolver"));
|
|
47
|
+
const LinkCacheModule = __importStar(require("../util/link-cache"));
|
|
48
|
+
const portfolio_1 = __importDefault(require("./portfolio/portfolio"));
|
|
49
|
+
const transaction_1 = __importDefault(require("./transaction/transaction"));
|
|
50
|
+
const uuid_1 = require("./utils/uuid");
|
|
51
|
+
const portfolio_pb_1 = require("../../fintekkers/models/portfolio/portfolio_pb");
|
|
52
|
+
const transaction_pb_1 = require("../../fintekkers/models/transaction/transaction_pb");
|
|
53
|
+
const local_timestamp_pb_1 = require("../../fintekkers/models/util/local_timestamp_pb");
|
|
54
|
+
const timestamp_pb_1 = require("google-protobuf/google/protobuf/timestamp_pb");
|
|
55
|
+
const query_portfolio_response_pb_1 = require("../../fintekkers/requests/portfolio/query_portfolio_response_pb");
|
|
56
|
+
const query_transaction_response_pb_1 = require("../../fintekkers/requests/transaction/query_transaction_response_pb");
|
|
57
|
+
function makeAsOf(seconds = 1700000000) {
|
|
58
|
+
const ts = new timestamp_pb_1.Timestamp();
|
|
59
|
+
ts.setSeconds(seconds);
|
|
60
|
+
ts.setNanos(0);
|
|
61
|
+
const lt = new local_timestamp_pb_1.LocalTimestampProto();
|
|
62
|
+
lt.setTimestamp(ts);
|
|
63
|
+
lt.setTimeZone("UTC");
|
|
64
|
+
return lt;
|
|
65
|
+
}
|
|
66
|
+
function mockPortfolioClient(resolved, log) {
|
|
67
|
+
return {
|
|
68
|
+
getByIds: (_req, cb) => {
|
|
69
|
+
log.count++;
|
|
70
|
+
const r = new query_portfolio_response_pb_1.QueryPortfolioResponseProto();
|
|
71
|
+
r.setPortfolioResponseList([resolved]);
|
|
72
|
+
setImmediate(() => cb(null, r));
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function mockTransactionClient(resolved, log) {
|
|
77
|
+
return {
|
|
78
|
+
getByIds: (_req, cb) => {
|
|
79
|
+
log.count++;
|
|
80
|
+
const r = new query_transaction_response_pb_1.QueryTransactionResponseProto();
|
|
81
|
+
r.setTransactionResponseList([resolved]);
|
|
82
|
+
setImmediate(() => cb(null, r));
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Throw-on-call client for the unused entity types.
|
|
87
|
+
function throwingClient(name) {
|
|
88
|
+
return {
|
|
89
|
+
getByIds: () => {
|
|
90
|
+
throw new Error(`${name} client should not be invoked in this test`);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
describe("lazy hydrate — concurrent hydrate() collapses to one RPC", () => {
|
|
95
|
+
beforeEach(() => {
|
|
96
|
+
LinkCacheModule.SECURITY.clear();
|
|
97
|
+
LinkCacheModule.PORTFOLIO.clear();
|
|
98
|
+
LinkCacheModule.TRANSACTION.clear();
|
|
99
|
+
link_resolver_1.default.setDefault(undefined);
|
|
100
|
+
});
|
|
101
|
+
test("Portfolio: 16 concurrent hydrate() calls → 1 RPC, all observe RESOLVED", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
102
|
+
const uuid = uuid_1.UUID.random();
|
|
103
|
+
const asOf = makeAsOf();
|
|
104
|
+
const resolved = new portfolio_pb_1.PortfolioProto();
|
|
105
|
+
resolved.setUuid(uuid.toUUIDProto());
|
|
106
|
+
resolved.setAsOf(asOf);
|
|
107
|
+
resolved.setIsLink(false);
|
|
108
|
+
resolved.setPortfolioName("RESOLVED");
|
|
109
|
+
const log = { count: 0 };
|
|
110
|
+
const resolver = new link_resolver_1.default({
|
|
111
|
+
portfolioClient: mockPortfolioClient(resolved, log),
|
|
112
|
+
securityClient: throwingClient("security"),
|
|
113
|
+
transactionClient: throwingClient("transaction"),
|
|
114
|
+
});
|
|
115
|
+
// Build 16 wrappers from the same link-mode proto.
|
|
116
|
+
const linkProto = new portfolio_pb_1.PortfolioProto();
|
|
117
|
+
linkProto.setUuid(uuid.toUUIDProto());
|
|
118
|
+
linkProto.setAsOf(asOf);
|
|
119
|
+
linkProto.setIsLink(true);
|
|
120
|
+
const wrappers = Array.from({ length: 16 }, () => new portfolio_1.default(linkProto));
|
|
121
|
+
const hydrated = yield Promise.all(wrappers.map((w) => w.hydrate(resolver)));
|
|
122
|
+
expect(log.count).toBe(1);
|
|
123
|
+
for (const w of hydrated) {
|
|
124
|
+
expect(w.getPortfolioName()).toBe("RESOLVED");
|
|
125
|
+
}
|
|
126
|
+
}));
|
|
127
|
+
test("Transaction: 16 concurrent hydrate() calls → 1 RPC, all observe resolved trade name", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
128
|
+
const uuid = uuid_1.UUID.random();
|
|
129
|
+
const asOf = makeAsOf();
|
|
130
|
+
const resolved = new transaction_pb_1.TransactionProto();
|
|
131
|
+
resolved.setUuid(uuid.toUUIDProto());
|
|
132
|
+
resolved.setAsOf(asOf);
|
|
133
|
+
resolved.setIsLink(false);
|
|
134
|
+
resolved.setTradeName("RESOLVED-TRADE");
|
|
135
|
+
const log = { count: 0 };
|
|
136
|
+
const resolver = new link_resolver_1.default({
|
|
137
|
+
transactionClient: mockTransactionClient(resolved, log),
|
|
138
|
+
securityClient: throwingClient("security"),
|
|
139
|
+
portfolioClient: throwingClient("portfolio"),
|
|
140
|
+
});
|
|
141
|
+
const linkProto = new transaction_pb_1.TransactionProto();
|
|
142
|
+
linkProto.setUuid(uuid.toUUIDProto());
|
|
143
|
+
linkProto.setAsOf(asOf);
|
|
144
|
+
linkProto.setIsLink(true);
|
|
145
|
+
const wrappers = Array.from({ length: 16 }, () => new transaction_1.default(linkProto));
|
|
146
|
+
const hydrated = yield Promise.all(wrappers.map((w) => w.hydrate(resolver)));
|
|
147
|
+
expect(log.count).toBe(1);
|
|
148
|
+
for (const w of hydrated) {
|
|
149
|
+
expect(w.proto.getTradeName()).toBe("RESOLVED-TRADE");
|
|
150
|
+
}
|
|
151
|
+
}));
|
|
152
|
+
});
|
|
153
|
+
//# sourceMappingURL=lazy-hydrate-race.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lazy-hydrate-race.test.js","sourceRoot":"","sources":["lazy-hydrate-race.test.ts"],"names":[],"mappings":"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"}
|