@fintekkers/ledger-models 0.4.47 → 0.4.48
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/price/Price.js +1 -1
- package/node/wrappers/models/price/Price.js.map +1 -1
- package/node/wrappers/models/price/Price.ts +2 -2
- package/node/wrappers/models/security/BondSecurity.js +3 -2
- package/node/wrappers/models/security/BondSecurity.js.map +1 -1
- package/node/wrappers/models/security/BondSecurity.ts +3 -2
- package/node/wrappers/models/security/FloatingRateNote.js +3 -2
- package/node/wrappers/models/security/FloatingRateNote.js.map +1 -1
- package/node/wrappers/models/security/FloatingRateNote.ts +3 -2
- package/node/wrappers/models/security/MortgageBackedSecurity.js +3 -2
- package/node/wrappers/models/security/MortgageBackedSecurity.js.map +1 -1
- package/node/wrappers/models/security/MortgageBackedSecurity.ts +3 -2
- package/node/wrappers/models/security/TIPSBond.js +3 -2
- package/node/wrappers/models/security/TIPSBond.js.map +1 -1
- package/node/wrappers/models/security/TIPSBond.ts +3 -2
- package/node/wrappers/models/security/identifier.js +2 -1
- package/node/wrappers/models/security/identifier.js.map +1 -1
- package/node/wrappers/models/security/identifier.ts +2 -1
- package/node/wrappers/models/security/security.js +3 -2
- package/node/wrappers/models/security/security.js.map +1 -1
- package/node/wrappers/models/security/security.ts +3 -2
- package/node/wrappers/util/object-types.test.js +435 -77
- package/node/wrappers/util/object-types.test.js.map +1 -1
- package/node/wrappers/util/object-types.test.ts +508 -66
- package/package.json +1 -1
|
@@ -40,6 +40,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
40
40
|
const fs = __importStar(require("fs"));
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
42
|
const decimal_js_1 = require("decimal.js");
|
|
43
|
+
const ts = __importStar(require("typescript"));
|
|
44
|
+
const jspb = __importStar(require("google-protobuf"));
|
|
43
45
|
const object_type_pb_1 = require("../../fintekkers/models/util/object_type_pb");
|
|
44
46
|
const security_pb_1 = require("../../fintekkers/models/security/security_pb");
|
|
45
47
|
const price_pb_1 = require("../../fintekkers/models/price/price_pb");
|
|
@@ -51,10 +53,25 @@ const query_security_response_pb_1 = require("../../fintekkers/requests/security
|
|
|
51
53
|
const query_portfolio_response_pb_1 = require("../../fintekkers/requests/portfolio/query_portfolio_response_pb");
|
|
52
54
|
const query_transaction_response_pb_1 = require("../../fintekkers/requests/transaction/query_transaction_response_pb");
|
|
53
55
|
const summary_pb_1 = require("../../fintekkers/requests/util/errors/summary_pb");
|
|
56
|
+
const product_type_pb_1 = require("../../fintekkers/models/security/product_type_pb");
|
|
57
|
+
const identifier_pb_1 = require("../../fintekkers/models/security/identifier/identifier_pb");
|
|
58
|
+
const coupon_type_pb_1 = require("../../fintekkers/models/security/coupon_type_pb");
|
|
59
|
+
const coupon_frequency_pb_1 = require("../../fintekkers/models/security/coupon_frequency_pb");
|
|
60
|
+
const index_type_pb_1 = require("../../fintekkers/models/security/index/index_type_pb");
|
|
61
|
+
const agency_pb_1 = require("../../fintekkers/models/security/bond/agency_pb");
|
|
62
|
+
const create_price_response_pb_1 = require("../../fintekkers/requests/price/create_price_response_pb");
|
|
63
|
+
const create_security_response_pb_1 = require("../../fintekkers/requests/security/create_security_response_pb");
|
|
64
|
+
const create_transaction_response_pb_1 = require("../../fintekkers/requests/transaction/create_transaction_response_pb");
|
|
65
|
+
const create_portfolio_response_pb_1 = require("../../fintekkers/requests/portfolio/create_portfolio_response_pb");
|
|
54
66
|
const objectTypes = __importStar(require("./object-types"));
|
|
55
67
|
const link_resolver_1 = __importDefault(require("./link-resolver"));
|
|
56
68
|
const Price_1 = __importDefault(require("../models/price/Price"));
|
|
57
69
|
const security_1 = __importDefault(require("../models/security/security"));
|
|
70
|
+
const BondSecurity_1 = __importStar(require("../models/security/BondSecurity"));
|
|
71
|
+
const TIPSBond_1 = __importDefault(require("../models/security/TIPSBond"));
|
|
72
|
+
const FloatingRateNote_1 = __importDefault(require("../models/security/FloatingRateNote"));
|
|
73
|
+
const MortgageBackedSecurity_1 = __importDefault(require("../models/security/MortgageBackedSecurity"));
|
|
74
|
+
const identifier_1 = require("../models/security/identifier");
|
|
58
75
|
const portfolio_1 = __importDefault(require("../models/portfolio/portfolio"));
|
|
59
76
|
const transaction_1 = __importDefault(require("../models/transaction/transaction"));
|
|
60
77
|
const transaction_type_1 = require("../models/transaction/transaction_type");
|
|
@@ -66,6 +83,7 @@ const PriceService_1 = require("../services/price-service/PriceService");
|
|
|
66
83
|
const SecurityService_1 = require("../services/security-service/SecurityService");
|
|
67
84
|
const TransactionService_1 = require("../services/transaction-service/TransactionService");
|
|
68
85
|
const PortfolioService_1 = require("../services/portfolio-service/PortfolioService");
|
|
86
|
+
const PositionService_1 = require("../services/position-service/PositionService");
|
|
69
87
|
// A PriceProto written by ledger-models v0.4.33: object_class / version
|
|
70
88
|
// strings in fields 1 and 2.
|
|
71
89
|
const OLD_PRICE = path.resolve(__dirname, '../../../../ledger-models-protos/fixtures/legacy_v0433/price.bin');
|
|
@@ -95,7 +113,7 @@ test('bytes from v0.4.33 are not readable', () => {
|
|
|
95
113
|
const old = new Uint8Array(fs.readFileSync(OLD_PRICE));
|
|
96
114
|
expect(() => price_pb_1.PriceProto.deserializeBinary(old)).toThrow();
|
|
97
115
|
});
|
|
98
|
-
// ---- Every library
|
|
116
|
+
// ---- Every library builder stamps what it builds (metric 2; LM-324) ----
|
|
99
117
|
/** A service with its gRPC client swapped for one that records each request. */
|
|
100
118
|
function capturingService(cls, response) {
|
|
101
119
|
const requests = [];
|
|
@@ -111,100 +129,440 @@ function capturingService(cls, response) {
|
|
|
111
129
|
};
|
|
112
130
|
const service = Object.create(cls.prototype);
|
|
113
131
|
service.client = {
|
|
114
|
-
validateCreateOrUpdate: unary, search: stream,
|
|
132
|
+
validateCreateOrUpdate: unary, createOrUpdate: unary, validateQueryRequest: unary, search: stream,
|
|
115
133
|
};
|
|
116
134
|
return { service, requests };
|
|
117
135
|
}
|
|
118
136
|
const asOf = datetime_1.ZonedDateTime.now().toProto();
|
|
119
137
|
const filter = () => new positionfilter_1.PositionFilter();
|
|
120
|
-
function security() {
|
|
121
|
-
return security_1.default.create(new security_pb_1.SecurityProto().setUuid(uuid_1.UUID.random().toUUIDProto()).setAssetClass('Equity'));
|
|
138
|
+
function security(caller) {
|
|
139
|
+
return security_1.default.create(caller(new security_pb_1.SecurityProto().setUuid(uuid_1.UUID.random().toUUIDProto()).setAssetClass('Equity')));
|
|
122
140
|
}
|
|
123
|
-
function portfolio() {
|
|
124
|
-
return new portfolio_1.default(new portfolio_pb_1.PortfolioProto().setUuid(uuid_1.UUID.random().toUUIDProto()).setPortfolioName('P').setAsOf(asOf));
|
|
141
|
+
function portfolio(caller) {
|
|
142
|
+
return new portfolio_1.default(caller(new portfolio_pb_1.PortfolioProto().setUuid(uuid_1.UUID.random().toUUIDProto()).setPortfolioName('P').setAsOf(asOf)));
|
|
125
143
|
}
|
|
126
|
-
|
|
144
|
+
const BOND_INPUTS = {
|
|
145
|
+
faceValue: new decimal_js_1.Decimal('1000'),
|
|
146
|
+
couponRate: new decimal_js_1.Decimal('0.05'),
|
|
147
|
+
couponType: coupon_type_pb_1.CouponTypeProto.FIXED,
|
|
148
|
+
couponFrequency: coupon_frequency_pb_1.CouponFrequencyProto.SEMIANNUALLY,
|
|
149
|
+
issueDate: new Date(2024, 0, 1),
|
|
150
|
+
maturityDate: new Date(2034, 0, 1),
|
|
151
|
+
};
|
|
152
|
+
function bond(caller) {
|
|
153
|
+
return security_1.default.create(caller(new security_pb_1.SecurityProto().setUuid(uuid_1.UUID.random().toUUIDProto())
|
|
154
|
+
.setProductType(product_type_pb_1.ProductTypeProto.TREASURY_NOTE).setBondDetails((0, BondSecurity_1.buildBondDetails)(BOND_INPUTS))));
|
|
155
|
+
}
|
|
156
|
+
function transaction(caller, onSecurity = security(caller)) {
|
|
157
|
+
return new transaction_1.default({
|
|
158
|
+
tradeDate: new Date(2024, 0, 15),
|
|
159
|
+
settlementDate: new Date(2024, 0, 17),
|
|
160
|
+
asOfDate: new Date(2024, 0, 15, 10, 30, 0),
|
|
161
|
+
price: new decimal_js_1.Decimal('100.50'),
|
|
162
|
+
security: onSecurity,
|
|
163
|
+
transactionType: new transaction_type_1.TransactionType(transaction_type_pb_1.TransactionTypeProto.BUY),
|
|
164
|
+
portfolio: portfolio(caller),
|
|
165
|
+
quantity: new decimal_js_1.Decimal('10'),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/** A LinkResolver whose clients record each request and find nothing. */
|
|
169
|
+
function capturingResolver() {
|
|
170
|
+
const requests = [];
|
|
171
|
+
const client = (response) => ({
|
|
172
|
+
getByIds: (request, callback) => {
|
|
173
|
+
requests.push(request);
|
|
174
|
+
callback(null, response());
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
const resolver = new link_resolver_1.default({
|
|
178
|
+
securityClient: client(() => new query_security_response_pb_1.QuerySecurityResponseProto()),
|
|
179
|
+
portfolioClient: client(() => new query_portfolio_response_pb_1.QueryPortfolioResponseProto()),
|
|
180
|
+
transactionClient: client(() => new query_transaction_response_pb_1.QueryTransactionResponseProto()),
|
|
181
|
+
});
|
|
182
|
+
return { resolver, requests };
|
|
183
|
+
}
|
|
184
|
+
/** The request a LinkResolver call sends. Nothing comes back, so a lookup may reject as not found. */
|
|
185
|
+
function resolverRequest(call) {
|
|
127
186
|
return __awaiter(this, void 0, void 0, function* () {
|
|
128
|
-
const requests =
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
callback(null, response());
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
|
-
const resolver = new link_resolver_1.default({
|
|
136
|
-
securityClient: client(() => new query_security_response_pb_1.QuerySecurityResponseProto()),
|
|
137
|
-
portfolioClient: client(() => new query_portfolio_response_pb_1.QueryPortfolioResponseProto()),
|
|
138
|
-
transactionClient: client(() => new query_transaction_response_pb_1.QueryTransactionResponseProto()),
|
|
139
|
-
});
|
|
140
|
-
// Nothing comes back, so each lookup rejects as not found.
|
|
141
|
-
yield expect(resolver.getSecurity(uuid_1.UUID.random())).rejects.toThrow();
|
|
142
|
-
yield expect(resolver.getPortfolio(uuid_1.UUID.random())).rejects.toThrow();
|
|
143
|
-
yield expect(resolver.getTransaction(uuid_1.UUID.random())).rejects.toThrow();
|
|
144
|
-
return requests;
|
|
187
|
+
const { resolver, requests } = capturingResolver();
|
|
188
|
+
yield call(resolver).catch(() => undefined);
|
|
189
|
+
expect(requests).toHaveLength(1);
|
|
190
|
+
return requests[0];
|
|
145
191
|
});
|
|
146
192
|
}
|
|
147
|
-
function lastRequest(cls, call) {
|
|
193
|
+
function lastRequest(cls, call, response = () => new summary_pb_1.SummaryProto()) {
|
|
148
194
|
return __awaiter(this, void 0, void 0, function* () {
|
|
149
|
-
const { service, requests } = capturingService(cls,
|
|
195
|
+
const { service, requests } = capturingService(cls, response);
|
|
150
196
|
yield call(service);
|
|
151
197
|
return requests[requests.length - 1];
|
|
152
198
|
});
|
|
153
199
|
}
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
200
|
+
const B = (name, type, build) => ({ name, type, build });
|
|
201
|
+
const T = object_type_pb_1.ObjectTypeProto;
|
|
202
|
+
const linkUuid = () => uuid_1.UUID.random();
|
|
203
|
+
const linkedPrice = (caller) => new Price_1.default(caller(new price_pb_1.PriceProto().setSecurity(security_1.default.linkOfLatest(linkUuid()))));
|
|
204
|
+
const linkedTransaction = (caller) => new transaction_1.default(caller(new transaction_pb_1.TransactionProto().setIsLink(true)
|
|
205
|
+
.setPortfolio(new portfolio_pb_1.PortfolioProto().setIsLink(true).setUuid(linkUuid().toUUIDProto()))));
|
|
206
|
+
/** Every public builder, by publicBuilders() key. */
|
|
207
|
+
const BUILDERS = [
|
|
208
|
+
B('Security.linkOf', T.OBJECT_TYPE_SECURITY, () => security_1.default.linkOf(linkUuid(), datetime_1.ZonedDateTime.now())),
|
|
209
|
+
B('Security.linkOfLatest', T.OBJECT_TYPE_SECURITY, () => security_1.default.linkOfLatest(linkUuid())),
|
|
210
|
+
B('BondSecurity.fromPricerInputs', T.OBJECT_TYPE_SECURITY, () => BondSecurity_1.default.fromPricerInputs(BOND_INPUTS)),
|
|
211
|
+
B('TIPSBond.fromPricerInputs', T.OBJECT_TYPE_SECURITY, () => TIPSBond_1.default.fromPricerInputs(Object.assign(Object.assign({}, BOND_INPUTS), { baseCpi: new decimal_js_1.Decimal('300'), indexDate: new Date(2024, 0, 1), inflationIndexType: index_type_pb_1.IndexTypeProto.CPI_U }))),
|
|
212
|
+
B('FloatingRateNote.fromPricerInputs', T.OBJECT_TYPE_SECURITY, () => FloatingRateNote_1.default.fromPricerInputs(Object.assign(Object.assign({}, BOND_INPUTS), { spread: new decimal_js_1.Decimal('0.001'), referenceRateIndex: index_type_pb_1.IndexTypeProto.SOFR, resetFrequency: coupon_frequency_pb_1.CouponFrequencyProto.MONTHLY }))),
|
|
213
|
+
B('MortgageBackedSecurity.fromPricerInputs', T.OBJECT_TYPE_SECURITY, () => MortgageBackedSecurity_1.default.fromPricerInputs(Object.assign(Object.assign({}, BOND_INPUTS), { poolNumber: 'FN AS1234', agency: agency_pb_1.AgencyProto.FNMA, wac: new decimal_js_1.Decimal('0.045'), wam: 358, passThroughRate: new decimal_js_1.Decimal('0.04'), currentFactor: new decimal_js_1.Decimal('0.95'), originalFaceValue: new decimal_js_1.Decimal('1000'), currentUpb: new decimal_js_1.Decimal('950'), psaSpeed: new decimal_js_1.Decimal('150') }))),
|
|
214
|
+
B('Identifier.fromName', T.OBJECT_TYPE_IDENTIFIER, () => identifier_1.Identifier.fromName('ISIN', 'US0378331005')),
|
|
215
|
+
B('Price.fromSimple', T.OBJECT_TYPE_PRICE, () => Price_1.default.fromSimple(uuid_1.UUID.random().toString(), 1, new Date())),
|
|
216
|
+
B('Price.create', T.OBJECT_TYPE_PRICE, (c) => Price_1.default.create(new decimal_js_1.Decimal(1), security(c), datetime_1.ZonedDateTime.now())),
|
|
217
|
+
B('Price.getCashPrice', T.OBJECT_TYPE_PRICE, (c) => Price_1.default.getCashPrice(security(c), datetime_1.ZonedDateTime.now())),
|
|
218
|
+
B('PositionFilter.toProto', T.OBJECT_TYPE_POSITION_FILTER, () => filter().toProto()),
|
|
219
|
+
B('QueryPositionRequest.toProto', T.OBJECT_TYPE_QUERY_POSITION_REQUEST, () => new QueryPositionRequest_1.QueryPositionRequest(filter(), position_pb_1.PositionTypeProto.TRANSACTION, position_pb_1.PositionViewProto.DEFAULT_VIEW, [], [], datetime_1.ZonedDateTime.now()).toProto()),
|
|
220
|
+
B('Transaction.constructor', T.OBJECT_TYPE_TRANSACTION, (c) => transaction(c)),
|
|
221
|
+
B('Transaction.createCashTransaction', T.OBJECT_TYPE_TRANSACTION, (c) => transaction_1.default.createCashTransaction(security(c), transaction(c))),
|
|
222
|
+
B('Transaction.addCashImpact', T.OBJECT_TYPE_TRANSACTION, (c) => {
|
|
223
|
+
const parent = transaction(c);
|
|
224
|
+
transaction_1.default.addCashImpact(parent, security(c));
|
|
225
|
+
return parent.getChildrenTransactions();
|
|
226
|
+
}),
|
|
227
|
+
B('Transaction.addMaturationTransaction', T.OBJECT_TYPE_TRANSACTION, (c) => {
|
|
228
|
+
const parent = transaction(c, bond(c));
|
|
229
|
+
transaction_1.default.addMaturationTransaction(parent, new transaction_type_1.TransactionType(transaction_type_pb_1.TransactionTypeProto.MATURATION), security(c));
|
|
230
|
+
return parent.getChildrenTransactions();
|
|
231
|
+
}),
|
|
232
|
+
B('Transaction.addDerivedTransactions', T.OBJECT_TYPE_TRANSACTION, (c) => {
|
|
233
|
+
const parent = transaction(c, bond(c));
|
|
234
|
+
transaction_1.default.addDerivedTransactions(parent, security(c));
|
|
235
|
+
return parent.getChildrenTransactions();
|
|
236
|
+
}),
|
|
237
|
+
B('PriceService.validateCreateOrUpdate', T.OBJECT_TYPE_CREATE_PRICE_REQUEST, (c) => lastRequest(PriceService_1.PriceService, (s) => s.validateCreateOrUpdate(c(new price_pb_1.PriceProto())))),
|
|
238
|
+
B('PriceService.createOrUpdate', T.OBJECT_TYPE_CREATE_PRICE_REQUEST, (c) => lastRequest(PriceService_1.PriceService, (s) => s.createOrUpdate(c(new price_pb_1.PriceProto())), () => new create_price_response_pb_1.CreatePriceResponseProto())),
|
|
239
|
+
B('PriceService.search', T.OBJECT_TYPE_QUERY_PRICE_REQUEST, () => lastRequest(PriceService_1.PriceService, (s) => s.search(asOf, filter()))),
|
|
240
|
+
B('PriceService.searchPrice', T.OBJECT_TYPE_QUERY_PRICE_REQUEST, () => lastRequest(PriceService_1.PriceService, (s) => s.searchPrice(asOf, filter()))),
|
|
241
|
+
B('PriceService.searchPriceAsOfNow', T.OBJECT_TYPE_QUERY_PRICE_REQUEST, () => lastRequest(PriceService_1.PriceService, (s) => s.searchPriceAsOfNow(filter()))),
|
|
242
|
+
B('PriceService.searchBySecurityId', T.OBJECT_TYPE_QUERY_PRICE_REQUEST, () => lastRequest(PriceService_1.PriceService, (s) => s.searchBySecurityId(uuid_1.UUID.random().toString(), asOf))),
|
|
243
|
+
B('PriceService.searchWithSecurities', T.OBJECT_TYPE_QUERY_PRICE_REQUEST, () => lastRequest(PriceService_1.PriceService, (s) => s.searchWithSecurities(asOf, filter(), capturingResolver().resolver))),
|
|
244
|
+
B('SecurityService.validateCreateSecurity', T.OBJECT_TYPE_CREATE_SECURITY_REQUEST, (c) => lastRequest(SecurityService_1.SecurityService, (s) => s.validateCreateSecurity(c(new security_pb_1.SecurityProto())))),
|
|
245
|
+
B('SecurityService.createSecurity', T.OBJECT_TYPE_CREATE_SECURITY_REQUEST, (c) => lastRequest(SecurityService_1.SecurityService, (s) => s.createSecurity(c(new security_pb_1.SecurityProto())), () => new create_security_response_pb_1.CreateSecurityResponseProto())),
|
|
246
|
+
B('SecurityService.searchSecurity', T.OBJECT_TYPE_QUERY_SECURITY_REQUEST, () => lastRequest(SecurityService_1.SecurityService, (s) => s.searchSecurity(asOf, filter()))),
|
|
247
|
+
B('SecurityService.searchSecurityAsOfNow', T.OBJECT_TYPE_QUERY_SECURITY_REQUEST, () => lastRequest(SecurityService_1.SecurityService, (s) => s.searchSecurityAsOfNow(filter()))),
|
|
248
|
+
B('TransactionService.validateCreateTransaction', T.OBJECT_TYPE_CREATE_TRANSACTION_REQUEST, (c) => lastRequest(TransactionService_1.TransactionService, (s) => s.validateCreateTransaction({ proto: c(new transaction_pb_1.TransactionProto()) }))),
|
|
249
|
+
B('TransactionService.createTransaction', T.OBJECT_TYPE_CREATE_TRANSACTION_REQUEST, (c) => lastRequest(TransactionService_1.TransactionService, (s) => s.createTransaction({ proto: c(new transaction_pb_1.TransactionProto()) }), () => new create_transaction_response_pb_1.CreateTransactionResponseProto())),
|
|
250
|
+
B('TransactionService.searchTransaction', T.OBJECT_TYPE_QUERY_TRANSACTION_REQUEST, () => lastRequest(TransactionService_1.TransactionService, (s) => s.searchTransaction(asOf, filter()))),
|
|
251
|
+
B('TransactionService.searchWithSecurityAndPortfolio', T.OBJECT_TYPE_QUERY_TRANSACTION_REQUEST, () => lastRequest(TransactionService_1.TransactionService, (s) => s.searchWithSecurityAndPortfolio(asOf, filter(), 100, capturingResolver().resolver))),
|
|
252
|
+
B('PortfolioService.validateCreatePortfolio', T.OBJECT_TYPE_CREATE_PORTFOLIO_REQUEST, (c) => lastRequest(PortfolioService_1.PortfolioService, (s) => s.validateCreatePortfolio(c(new portfolio_pb_1.PortfolioProto())))),
|
|
253
|
+
B('PortfolioService.createPortfolio', T.OBJECT_TYPE_CREATE_PORTFOLIO_REQUEST, (c) => lastRequest(PortfolioService_1.PortfolioService, (s) => s.createPortfolio(c(new portfolio_pb_1.PortfolioProto())), () => new create_portfolio_response_pb_1.CreatePortfolioResponseProto())),
|
|
254
|
+
B('PortfolioService.searchPortfolio', T.OBJECT_TYPE_QUERY_PORTFOLIO_REQUEST, () => lastRequest(PortfolioService_1.PortfolioService, (s) => s.searchPortfolio(asOf, filter()))),
|
|
255
|
+
B('PositionService.validateRequest', T.OBJECT_TYPE_QUERY_POSITION_REQUEST, () => lastRequest(PositionService_1.PositionService, (s) => s.validateRequest(QueryPositionRequest_1.QueryPositionRequest.from([], [])))),
|
|
256
|
+
B('PositionService.search', T.OBJECT_TYPE_QUERY_POSITION_REQUEST, () => lastRequest(PositionService_1.PositionService, (s) => s.search(QueryPositionRequest_1.QueryPositionRequest.from([], [])))),
|
|
257
|
+
B('LinkResolver.getSecurity', T.OBJECT_TYPE_QUERY_SECURITY_REQUEST, () => resolverRequest((r) => r.getSecurity(linkUuid()))),
|
|
258
|
+
B('LinkResolver.getPortfolio', T.OBJECT_TYPE_QUERY_PORTFOLIO_REQUEST, () => resolverRequest((r) => r.getPortfolio(linkUuid()))),
|
|
259
|
+
B('LinkResolver.getTransaction', T.OBJECT_TYPE_QUERY_TRANSACTION_REQUEST, () => resolverRequest((r) => r.getTransaction(linkUuid()))),
|
|
260
|
+
B('LinkResolver.resolveSecurities', T.OBJECT_TYPE_QUERY_SECURITY_REQUEST, (c) => resolverRequest((r) => r.resolveSecurities([linkedPrice(c)]))),
|
|
261
|
+
B('LinkResolver.resolvePortfolios', T.OBJECT_TYPE_QUERY_PORTFOLIO_REQUEST, (c) => resolverRequest((r) => r.resolvePortfolios([linkedTransaction(c)]))),
|
|
191
262
|
];
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
transactionType: new transaction_type_1.TransactionType(transaction_type_pb_1.TransactionTypeProto.BUY),
|
|
200
|
-
portfolio: portfolio(),
|
|
201
|
-
quantity: new decimal_js_1.Decimal('10'),
|
|
263
|
+
/** The messages a builder returned: wrappers are read through `proto`, arrays item by item. */
|
|
264
|
+
function messagesOf(built) {
|
|
265
|
+
return (Array.isArray(built) ? built : [built]).map((item) => {
|
|
266
|
+
const message = item instanceof jspb.Message ? item : item === null || item === void 0 ? void 0 : item.proto;
|
|
267
|
+
if (!(message instanceof jspb.Message))
|
|
268
|
+
throw new Error(`not a message or a wrapper of one: ${String(item)}`);
|
|
269
|
+
return message;
|
|
202
270
|
});
|
|
203
271
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
272
|
+
// jspb.Message's own getters (getExtension, getJsPbMessageId, ...) are not fields.
|
|
273
|
+
const JSPB_GETTERS = new Set(Object.getOwnPropertyNames(jspb.Message.prototype));
|
|
274
|
+
/** The zero-argument field getters of a generated message. */
|
|
275
|
+
function fieldGetters(message) {
|
|
276
|
+
const names = [];
|
|
277
|
+
for (let proto = Object.getPrototypeOf(message); proto && proto !== jspb.Message.prototype; proto = Object.getPrototypeOf(proto)) {
|
|
278
|
+
for (const name of Object.getOwnPropertyNames(proto)) {
|
|
279
|
+
const getter = proto[name];
|
|
280
|
+
if (name.startsWith('get') && !JSPB_GETTERS.has(name) && typeof getter === 'function' && getter.length === 0)
|
|
281
|
+
names.push(name);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return names;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Visits `root` and every nested message (singular and repeated fields; no
|
|
288
|
+
* message with object_type has a map field of messages today). `callers`
|
|
289
|
+
* holds the protos the caller passed in: they are passed through as is, so
|
|
290
|
+
* they and what they hold are not checked here.
|
|
291
|
+
*/
|
|
292
|
+
function untypedMessages(root, callers = new Set()) {
|
|
293
|
+
const walk = { untyped: [], typed: 0 };
|
|
294
|
+
(function visit(value, at) {
|
|
295
|
+
if (Array.isArray(value)) {
|
|
296
|
+
value.forEach((item, i) => visit(item, `${at}[${i}]`));
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (!(value instanceof jspb.Message) || callers.has(value))
|
|
300
|
+
return;
|
|
301
|
+
const own = objectTypes.ownType(value);
|
|
302
|
+
if (own !== undefined) {
|
|
303
|
+
walk.typed++;
|
|
304
|
+
const message = value;
|
|
305
|
+
if (objectTypes.readType(message) !== own || String(objectTypes.readVersion(message)) !== String(objectTypes.SCHEMA_VERSION)) {
|
|
306
|
+
walk.untyped.push(at);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
for (const getter of fieldGetters(value)) {
|
|
310
|
+
visit(value[getter](), `${at}.${getter}`);
|
|
311
|
+
}
|
|
312
|
+
})(root, '$');
|
|
313
|
+
return walk;
|
|
314
|
+
}
|
|
315
|
+
test.each(BUILDERS.map((row) => [row.name, row]))('%s stamps every message it builds', (_name, row) => __awaiter(void 0, void 0, void 0, function* () {
|
|
316
|
+
const callers = new Set();
|
|
317
|
+
const messages = messagesOf(yield row.build((proto) => { callers.add(proto); return proto; }));
|
|
318
|
+
expect(messages.length).toBeGreaterThan(0);
|
|
319
|
+
for (const message of messages) {
|
|
320
|
+
expect(objectTypes.readType(message)).toBe(row.type);
|
|
321
|
+
const walk = untypedMessages(message, callers);
|
|
322
|
+
expect(walk.untyped).toEqual([]);
|
|
323
|
+
expect(walk.typed).toBeGreaterThan(0);
|
|
324
|
+
}
|
|
325
|
+
// Callers' protos are not re-stamped (LM-322's pass-through ruling).
|
|
326
|
+
for (const proto of callers) {
|
|
327
|
+
expect(objectTypes.readType(proto)).toBe(object_type_pb_1.ObjectTypeProto.OBJECT_TYPE_UNKNOWN);
|
|
328
|
+
expect(objectTypes.readVersion(proto)).toBeUndefined();
|
|
329
|
+
}
|
|
208
330
|
expect(objectTypes.SCHEMA_VERSION).toEqual([0, 0, 1]);
|
|
209
331
|
}));
|
|
332
|
+
test("callers' protos pass through as the same unstamped object", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
333
|
+
const securityProto = new security_pb_1.SecurityProto().setUuid(uuid_1.UUID.random().toUUIDProto());
|
|
334
|
+
const price = Price_1.default.create(new decimal_js_1.Decimal(1), new security_1.default(securityProto), datetime_1.ZonedDateTime.now());
|
|
335
|
+
expect(price.proto.getSecurity()).toBe(securityProto);
|
|
336
|
+
const callers = [];
|
|
337
|
+
const txn = transaction((proto) => { callers.push(proto); return proto; });
|
|
338
|
+
const [txnSecurity, txnPortfolio] = callers;
|
|
339
|
+
expect(txn.proto.getSecurity()).toBe(txnSecurity);
|
|
340
|
+
expect(txn.proto.getPortfolio()).toBe(txnPortfolio);
|
|
341
|
+
const input = new security_pb_1.SecurityProto();
|
|
342
|
+
const request = yield lastRequest(SecurityService_1.SecurityService, (s) => s.validateCreateSecurity(input));
|
|
343
|
+
expect(request.getSecurityInput()).toBe(input);
|
|
344
|
+
for (const proto of [securityProto, txnSecurity, txnPortfolio, input]) {
|
|
345
|
+
expect(objectTypes.readType(proto)).toBe(object_type_pb_1.ObjectTypeProto.OBJECT_TYPE_UNKNOWN);
|
|
346
|
+
expect(objectTypes.readVersion(proto)).toBeUndefined();
|
|
347
|
+
}
|
|
348
|
+
}));
|
|
349
|
+
test('Price.fromSimple builds its security link, so the link is stamped, not passed through', () => {
|
|
350
|
+
const callers = new Set();
|
|
351
|
+
const row = BUILDERS.find((b) => b.name === 'Price.fromSimple');
|
|
352
|
+
const price = messagesOf(row.build((proto) => { callers.add(proto); return proto; }))[0];
|
|
353
|
+
expect(callers.size).toBe(0);
|
|
354
|
+
const link = price.getSecurity();
|
|
355
|
+
expect(objectTypes.readType(link)).toBe(object_type_pb_1.ObjectTypeProto.OBJECT_TYPE_SECURITY);
|
|
356
|
+
expect(objectTypes.readVersion(link)).toEqual(objectTypes.SCHEMA_VERSION);
|
|
357
|
+
});
|
|
358
|
+
// ---- The walker finds what it must (it cannot pass by not looking) ----
|
|
359
|
+
const stampedPrice = () => objectTypes.stampOwn(new price_pb_1.PriceProto());
|
|
360
|
+
const stampedSecurity = () => objectTypes.stampOwn(new security_pb_1.SecurityProto());
|
|
361
|
+
test('the walker reports an unstamped nested message', () => {
|
|
362
|
+
const walk = untypedMessages(stampedPrice().setSecurity(new security_pb_1.SecurityProto()));
|
|
363
|
+
expect(walk.untyped).toEqual(['$.getSecurity']);
|
|
364
|
+
expect(walk.typed).toBe(2);
|
|
365
|
+
});
|
|
366
|
+
test('the walker reports an unstamped message in a repeated field', () => {
|
|
367
|
+
const security = stampedSecurity().setIdentifiersList([objectTypes.stampOwn(new identifier_pb_1.IdentifierProto()), new identifier_pb_1.IdentifierProto()]);
|
|
368
|
+
expect(untypedMessages(security).untyped).toEqual(['$.getIdentifiersList[1]']);
|
|
369
|
+
});
|
|
370
|
+
test('the walker reports a message stamped with another type', () => {
|
|
371
|
+
const security = objectTypes.stamp(new security_pb_1.SecurityProto(), object_type_pb_1.ObjectTypeProto.OBJECT_TYPE_PRICE);
|
|
372
|
+
expect(untypedMessages(stampedPrice().setSecurity(security)).untyped).toEqual(['$.getSecurity']);
|
|
373
|
+
});
|
|
374
|
+
test('the walker reports a message with its type but no version', () => {
|
|
375
|
+
const security = new security_pb_1.SecurityProto().setObjectType(object_type_pb_1.ObjectTypeProto.OBJECT_TYPE_SECURITY);
|
|
376
|
+
expect(untypedMessages(stampedPrice().setSecurity(security)).untyped).toEqual(['$.getSecurity']);
|
|
377
|
+
});
|
|
378
|
+
test("the walker skips callers' protos", () => {
|
|
379
|
+
const input = new security_pb_1.SecurityProto();
|
|
380
|
+
expect(untypedMessages(stampedPrice().setSecurity(input), new Set([input]))).toEqual({ untyped: [], typed: 1 });
|
|
381
|
+
});
|
|
382
|
+
// ---- Every public builder is tested or excluded (metric line 2) ----
|
|
383
|
+
const WRAPPERS_DIR = path.resolve(__dirname, '..');
|
|
384
|
+
/** Every non-test, non-declaration .ts under node/wrappers/. The generated fintekkers/**_pb are not scanned. */
|
|
385
|
+
function wrapperSources(dir = WRAPPERS_DIR) {
|
|
386
|
+
return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
387
|
+
const file = path.join(dir, entry.name);
|
|
388
|
+
if (entry.isDirectory())
|
|
389
|
+
return wrapperSources(file);
|
|
390
|
+
return /\.ts$/.test(entry.name) && !/\.(test|d)\.ts$/.test(entry.name) ? [file] : [];
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Public callables under node/wrappers/ that build a message, keyed
|
|
395
|
+
* `Class.method`, `Class.constructor` or `module.fn`, each with the rule that
|
|
396
|
+
* flagged it. Builders are found by type, not by name:
|
|
397
|
+
* (a) the return type holds an ObjectTyped message (after Promise, array, union);
|
|
398
|
+
* (b) the return type holds a wrapper whose `proto` is one;
|
|
399
|
+
* (c) the body has `new X()` where X is (a) or (b);
|
|
400
|
+
* (d) the body calls a flagged callable, public or not, repeated to a fixpoint.
|
|
401
|
+
* The service sources are scanned; only their integration tests are excluded
|
|
402
|
+
* from `npm test` (jest.config.js).
|
|
403
|
+
*/
|
|
404
|
+
function publicBuilders() {
|
|
405
|
+
var _a;
|
|
406
|
+
const configFile = path.resolve(WRAPPERS_DIR, '../../tsconfig.json');
|
|
407
|
+
const { config } = ts.readConfigFile(configFile, ts.sys.readFile);
|
|
408
|
+
const { options } = ts.parseJsonConfigFileContent(config, ts.sys, path.dirname(configFile));
|
|
409
|
+
const sources = wrapperSources();
|
|
410
|
+
const program = ts.createProgram(sources, Object.assign(Object.assign({}, options), { noEmit: true }));
|
|
411
|
+
const checker = program.getTypeChecker();
|
|
412
|
+
const isMessage = (t) => !!t.getProperty('getObjectType') && !!t.getProperty('setVersion');
|
|
413
|
+
const isWrapper = (t) => {
|
|
414
|
+
const proto = t.getProperty('proto');
|
|
415
|
+
return !!(proto === null || proto === void 0 ? void 0 : proto.valueDeclaration) && isMessage(checker.getTypeOfSymbolAtLocation(proto, proto.valueDeclaration));
|
|
416
|
+
};
|
|
417
|
+
// The (a) or (b) rule a type meets, or undefined.
|
|
418
|
+
const builtBy = (t) => {
|
|
419
|
+
var _a;
|
|
420
|
+
const parts = t.isUnion() ? t.types : [t];
|
|
421
|
+
for (const part of parts) {
|
|
422
|
+
const unwrap = checker.isArrayType(part) || ((_a = part.getSymbol()) === null || _a === void 0 ? void 0 : _a.getName()) === 'Promise';
|
|
423
|
+
const inner = unwrap ? checker.getTypeArguments(part)[0] : part;
|
|
424
|
+
if (inner !== part) {
|
|
425
|
+
const rule = builtBy(inner);
|
|
426
|
+
if (rule)
|
|
427
|
+
return rule;
|
|
428
|
+
}
|
|
429
|
+
else if (isMessage(part)) {
|
|
430
|
+
return `(a) returns ${checker.typeToString(part)}`;
|
|
431
|
+
}
|
|
432
|
+
else if (isWrapper(part)) {
|
|
433
|
+
return `(b) returns ${checker.typeToString(part)}, which wraps a message`;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return undefined;
|
|
437
|
+
};
|
|
438
|
+
const callables = new Map();
|
|
439
|
+
const isPrivate = (m) => (!!m.name && ts.isPrivateIdentifier(m.name))
|
|
440
|
+
|| (ts.getCombinedModifierFlags(m) & (ts.ModifierFlags.Private | ts.ModifierFlags.Protected)) !== 0;
|
|
441
|
+
for (const sourceFile of program.getSourceFiles().filter((f) => sources.includes(path.resolve(f.fileName)))) {
|
|
442
|
+
const moduleName = path.basename(sourceFile.fileName, '.ts');
|
|
443
|
+
const exported = new Set();
|
|
444
|
+
const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
|
|
445
|
+
for (let symbol of moduleSymbol ? checker.getExportsOfModule(moduleSymbol) : []) {
|
|
446
|
+
if (symbol.flags & ts.SymbolFlags.Alias)
|
|
447
|
+
symbol = checker.getAliasedSymbol(symbol);
|
|
448
|
+
(_a = symbol.declarations) === null || _a === void 0 ? void 0 : _a.forEach((d) => exported.add(d));
|
|
449
|
+
}
|
|
450
|
+
const signatureOf = (decl) => checker.getReturnTypeOfSignature(checker.getSignatureFromDeclaration(decl));
|
|
451
|
+
sourceFile.forEachChild(function visit(node) {
|
|
452
|
+
if (ts.isFunctionDeclaration(node) && node.name && node.body) {
|
|
453
|
+
callables.set(node, { key: `${moduleName}.${node.name.text}`, isPublic: exported.has(node), returns: signatureOf(node), bodies: [node.body] });
|
|
454
|
+
}
|
|
455
|
+
else if (ts.isVariableStatement(node)) {
|
|
456
|
+
for (const d of node.declarationList.declarations) {
|
|
457
|
+
if (ts.isIdentifier(d.name) && d.initializer && (ts.isArrowFunction(d.initializer) || ts.isFunctionExpression(d.initializer))) {
|
|
458
|
+
callables.set(d.initializer, { key: `${moduleName}.${d.name.text}`, isPublic: exported.has(d), returns: signatureOf(d.initializer), bodies: [d.initializer.body] });
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
else if (ts.isClassDeclaration(node) && node.name) {
|
|
463
|
+
const className = node.name.text;
|
|
464
|
+
const isPublic = exported.has(node);
|
|
465
|
+
const ctor = node.members.find(ts.isConstructorDeclaration);
|
|
466
|
+
const fieldInits = node.members.flatMap((m) => ts.isPropertyDeclaration(m) && m.initializer && !(ts.getCombinedModifierFlags(m) & ts.ModifierFlags.Static) ? [m.initializer] : []);
|
|
467
|
+
callables.set(ctor !== null && ctor !== void 0 ? ctor : node, {
|
|
468
|
+
key: `${className}.constructor`, isPublic,
|
|
469
|
+
returns: checker.getDeclaredTypeOfSymbol(checker.getSymbolAtLocation(node.name)),
|
|
470
|
+
bodies: [...((ctor === null || ctor === void 0 ? void 0 : ctor.body) ? [ctor.body] : []), ...fieldInits],
|
|
471
|
+
});
|
|
472
|
+
for (const m of node.members) {
|
|
473
|
+
if ((ts.isMethodDeclaration(m) || ts.isGetAccessorDeclaration(m)) && m.body && ts.isIdentifier(m.name)) {
|
|
474
|
+
callables.set(m, { key: `${className}.${m.name.text}`, isPublic: isPublic && !isPrivate(m), returns: signatureOf(m), bodies: [m.body] });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
const flagged = new Map();
|
|
481
|
+
for (const [decl, c] of callables) {
|
|
482
|
+
const rule = builtBy(c.returns);
|
|
483
|
+
if (rule)
|
|
484
|
+
flagged.set(decl, rule);
|
|
485
|
+
}
|
|
486
|
+
for (const [decl, c] of callables) {
|
|
487
|
+
if (flagged.has(decl))
|
|
488
|
+
continue;
|
|
489
|
+
c.bodies.forEach(function visit(node) {
|
|
490
|
+
if (flagged.has(decl))
|
|
491
|
+
return;
|
|
492
|
+
if (ts.isNewExpression(node)) {
|
|
493
|
+
const rule = builtBy(checker.getTypeAtLocation(node));
|
|
494
|
+
if (rule)
|
|
495
|
+
flagged.set(decl, `(c) ${node.getText().split('(')[0]}(): ${rule}`);
|
|
496
|
+
}
|
|
497
|
+
node.forEachChild(visit);
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
// (d): calls to a flagged callable. `new X()` of a flagged class is already (c).
|
|
501
|
+
for (let changed = true; changed;) {
|
|
502
|
+
changed = false;
|
|
503
|
+
for (const [decl, c] of callables) {
|
|
504
|
+
if (flagged.has(decl))
|
|
505
|
+
continue;
|
|
506
|
+
c.bodies.forEach(function visit(node) {
|
|
507
|
+
var _a;
|
|
508
|
+
if (flagged.has(decl))
|
|
509
|
+
return;
|
|
510
|
+
if (ts.isCallExpression(node) || ts.isNewExpression(node)) {
|
|
511
|
+
const target = (_a = checker.getResolvedSignature(node)) === null || _a === void 0 ? void 0 : _a.declaration;
|
|
512
|
+
if (target && flagged.has(target)) {
|
|
513
|
+
flagged.set(decl, `(d) calls ${callables.get(target).key}`);
|
|
514
|
+
changed = true;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
node.forEachChild(visit);
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
const builders = new Map();
|
|
522
|
+
for (const [decl, rule] of flagged) {
|
|
523
|
+
const c = callables.get(decl);
|
|
524
|
+
if (c.isPublic)
|
|
525
|
+
builders.set(c.key, rule);
|
|
526
|
+
}
|
|
527
|
+
return builders;
|
|
528
|
+
}
|
|
529
|
+
const RETURNS_STORED = 'an accessor: returns or wraps a proto it holds; builds nothing';
|
|
530
|
+
const WRAPS_CALLER = "wraps the caller's proto (pass-through ruling)";
|
|
531
|
+
const SERIALIZES = "converts values to untyped messages (dates, UUIDs, decimals) or returns the caller's Identifier proto as is";
|
|
532
|
+
const DESERIALIZES = 'decodes protos the caller or server sent into wrappers; builds no message';
|
|
533
|
+
/** Public callables the scan flags that build no message to stamp, each with its reason. */
|
|
534
|
+
const EXCLUSIONS = Object.assign(Object.assign({ 'object-types.stamp': 'the stamping helper itself (tested above)', 'object-types.stampOwn': 'the stamping helper itself (tested above)', 'price_rules.validate': 'local stand-in for null input; never returned or sent', 'price_rules.requireValid': 'local stand-in for null input; never returned or sent', 'Security.create': "picks the wrapper class for the caller's proto (pass-through ruling)", 'Position.fromJSON': DESERIALIZES, 'ProtoSerializationUtil.deserialize': DESERIALIZES, 'serialization.util.unpack': DESERIALIZES, 'LocalDate.toDate': DESERIALIZES, 'ProtoSerializationUtil.serialize': SERIALIZES, 'serialization.util.pack': SERIALIZES, 'PositionFilter.addFilter': SERIALIZES, 'PositionFilter.addEqualsFilter': SERIALIZES, 'PositionFilter.addEqualsStringFilter': SERIALIZES, 'PositionFilter.addObjectFilter': SERIALIZES, 'Security.hydrate': 'swaps in the proto LinkResolver.getSecurity fetched (that request is tested)', 'Portfolio.hydrate': 'swaps in the proto LinkResolver.getPortfolio fetched (that request is tested)', 'Transaction.hydrate': 'swaps in the proto LinkResolver.getTransaction fetched (that request is tested)' }, Object.fromEntries([
|
|
535
|
+
'BondSecurity.constructor', 'FloatingRateNote.constructor', 'Identifier.constructor', 'IndexSecurity.constructor',
|
|
536
|
+
'Issuance.constructor', 'MortgageBackedSecurity.constructor', 'Portfolio.constructor', 'Position.constructor',
|
|
537
|
+
'Price.constructor', 'Security.constructor', 'TIPSBond.constructor',
|
|
538
|
+
].map((key) => [key, WRAPS_CALLER]))), Object.fromEntries([
|
|
539
|
+
'BondSecurity.getIssuances', 'Price.getSecurity',
|
|
540
|
+
'Position.getField', 'Position.getFieldDisplay', 'Position.getFieldValue', 'Position.getMeasureValue', 'Position.toString',
|
|
541
|
+
'Security.getField', 'Security.getIdentifierByType', 'Security.getIdentifiers', 'Security.toString',
|
|
542
|
+
'Transaction.getCashTransaction', 'Transaction.getChildrenTransactions', 'Transaction.getField', 'Transaction.getIssuerName',
|
|
543
|
+
'Transaction.getPortfolio', 'Transaction.getPrice', 'Transaction.getSecurity', 'Transaction.getStrategyAllocation',
|
|
544
|
+
'Transaction.isCashSecurity', 'Transaction.toString',
|
|
545
|
+
].map((key) => [key, RETURNS_STORED])));
|
|
546
|
+
describe('every public builder is tested or excluded (metric line 2)', () => {
|
|
547
|
+
let scanned;
|
|
548
|
+
beforeAll(() => { scanned = publicBuilders(); }, 60000);
|
|
549
|
+
test('every public builder is tested or excluded', () => {
|
|
550
|
+
const covered = new Set([...BUILDERS.map((b) => b.name), ...Object.keys(EXCLUSIONS)]);
|
|
551
|
+
const missing = [...scanned].filter(([key]) => !covered.has(key)).map(([key, rule]) => `${key}: rule ${rule}`);
|
|
552
|
+
expect(missing).toEqual([]);
|
|
553
|
+
});
|
|
554
|
+
test('no stale rows', () => {
|
|
555
|
+
const stale = [...BUILDERS.map((b) => b.name), ...Object.keys(EXCLUSIONS)].filter((key) => !scanned.has(key));
|
|
556
|
+
expect(stale).toEqual([]);
|
|
557
|
+
});
|
|
558
|
+
test('every exclusion has a reason', () => {
|
|
559
|
+
const blank = Object.entries(EXCLUSIONS).filter(([, reason]) => reason.trim().length === 0).map(([key]) => key);
|
|
560
|
+
expect(blank).toEqual([]);
|
|
561
|
+
});
|
|
562
|
+
test('no builder is both tested and excluded, and none is listed twice', () => {
|
|
563
|
+
const names = BUILDERS.map((b) => b.name);
|
|
564
|
+
expect(names.filter((name, i) => names.indexOf(name) !== i)).toEqual([]);
|
|
565
|
+
expect(names.filter((name) => Object.prototype.hasOwnProperty.call(EXCLUSIONS, name))).toEqual([]);
|
|
566
|
+
});
|
|
567
|
+
});
|
|
210
568
|
//# sourceMappingURL=object-types.test.js.map
|