@fintekkers/ledger-models 0.4.29 → 0.4.30

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.
@@ -15,8 +15,10 @@ export interface FilterableRow {
15
15
  * ASSET_CLASS with EQUALS / NOT_EQUALS uses the shared assetClassMatches
16
16
  * rule, so a group code such as FIXED_INCOME matches its members and labels;
17
17
  * every other field compares exactly. A null or undefined stored value never
18
- * throws: EQUALS and the ordering operators drop the row, NOT_EQUALS keeps
19
- * it. An unknown operator matches nothing. Errors raised by row.getField
18
+ * throws and drops the row for every operator, except ASSET_CLASS with
19
+ * NOT_EQUALS, which keeps it (LM-281). The null check runs before the
20
+ * ASSET_CLASS branch, so it handles that asset-class exception itself
21
+ * (LM-284). An unknown operator matches nothing. Errors raised by row.getField
20
22
  * propagate (a row represents an absent value by returning null).
21
23
  */
22
24
  export declare function matches(field: FieldProto, operator: PositionFilterOperator, filterValue: any, storedValue: any): boolean;
@@ -9,13 +9,15 @@ const product_hierarchy_1 = require("../security/product_hierarchy");
9
9
  * ASSET_CLASS with EQUALS / NOT_EQUALS uses the shared assetClassMatches
10
10
  * rule, so a group code such as FIXED_INCOME matches its members and labels;
11
11
  * every other field compares exactly. A null or undefined stored value never
12
- * throws: EQUALS and the ordering operators drop the row, NOT_EQUALS keeps
13
- * it. An unknown operator matches nothing. Errors raised by row.getField
12
+ * throws and drops the row for every operator, except ASSET_CLASS with
13
+ * NOT_EQUALS, which keeps it (LM-281). The null check runs before the
14
+ * ASSET_CLASS branch, so it handles that asset-class exception itself
15
+ * (LM-284). An unknown operator matches nothing. Errors raised by row.getField
14
16
  * propagate (a row represents an absent value by returning null).
15
17
  */
16
18
  function matches(field, operator, filterValue, storedValue) {
17
19
  if (storedValue === null || storedValue === undefined) {
18
- return operator === position_util_pb_1.PositionFilterOperator.NOT_EQUALS;
20
+ return field === field_pb_1.FieldProto.ASSET_CLASS && operator === position_util_pb_1.PositionFilterOperator.NOT_EQUALS;
19
21
  }
20
22
  if (field === field_pb_1.FieldProto.ASSET_CLASS &&
21
23
  (operator === position_util_pb_1.PositionFilterOperator.EQUALS ||
@@ -1,19 +1,46 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
2
29
  /**
3
30
  * LM-281: the JS filter helper matches Java's PositionFilter row for row.
4
31
  *
5
32
  * ASSET_CLASS EQUALS / NOT_EQUALS use the shared assetClassMatches rule;
6
33
  * every other field compares exactly. A null stored value never throws.
7
34
  */
8
- var __importDefault = (this && this.__importDefault) || function (mod) {
9
- return (mod && mod.__esModule) ? mod : { "default": mod };
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
35
+ const fs = __importStar(require("fs"));
36
+ const path = __importStar(require("path"));
12
37
  const field_pb_1 = require("../../../fintekkers/models/position/field_pb");
13
38
  const position_util_pb_1 = require("../../../fintekkers/models/position/position_util_pb");
14
39
  const security_pb_1 = require("../../../fintekkers/models/security/security_pb");
15
40
  const security_1 = __importDefault(require("../security/security"));
16
41
  const position_matcher_1 = require("./position_matcher");
42
+ const PROTOS = path.resolve(__dirname, '../../../../../ledger-models-protos');
43
+ const nullCases = JSON.parse(fs.readFileSync(path.join(PROTOS, 'fixtures/position_filter_null_cases.json'), 'utf-8')).cases;
17
44
  function securityRow(assetClass) {
18
45
  const proto = new security_pb_1.SecurityProto();
19
46
  proto.setAssetClass(assetClass);
@@ -58,7 +85,26 @@ describe('position_matcher', () => {
58
85
  expect((0, position_matcher_1.matches)(field_pb_1.FieldProto.MATURITY_DATE, position_util_pb_1.PositionFilterOperator.MORE_THAN, 1, 2)).toBe(true);
59
86
  expect((0, position_matcher_1.matches)(field_pb_1.FieldProto.MATURITY_DATE, position_util_pb_1.PositionFilterOperator.MORE_THAN, 2, 1)).toBe(false);
60
87
  expect((0, position_matcher_1.matches)(field_pb_1.FieldProto.PORTFOLIO_NAME, position_util_pb_1.PositionFilterOperator.EQUALS, 'a', null)).toBe(false);
61
- expect((0, position_matcher_1.matches)(field_pb_1.FieldProto.PORTFOLIO_NAME, position_util_pb_1.PositionFilterOperator.NOT_EQUALS, 'a', null)).toBe(true);
88
+ expect((0, position_matcher_1.matches)(field_pb_1.FieldProto.PORTFOLIO_NAME, position_util_pb_1.PositionFilterOperator.NOT_EQUALS, 'a', null)).toBe(false);
89
+ });
90
+ // LM-284: NOT_EQUALS drops a null on every field but ASSET_CLASS. Cases
91
+ // come from ledger-models-protos/fixtures/position_filter_null_cases.json,
92
+ // shared with the Java, Python and Rust tests.
93
+ test('the shared null-cases fixture has its cases', () => {
94
+ expect(nullCases.length).toBeGreaterThanOrEqual(7);
95
+ });
96
+ test.each(nullCases)('$field $operator $filter on null -> $expected', (c) => {
97
+ const field = field_pb_1.FieldProto[c.field];
98
+ const operator = position_util_pb_1.PositionFilterOperator[c.operator];
99
+ expect(field).toBeDefined();
100
+ expect(operator).toBeDefined();
101
+ expect((0, position_matcher_1.matches)(field, operator, c.filter, null)).toBe(c.expected);
102
+ });
103
+ test('filterRows NOT_EQUALS drops a row with a null on another field', () => {
104
+ const filters = [
105
+ { field: field_pb_1.FieldProto.PORTFOLIO_NAME, operator: position_util_pb_1.PositionFilterOperator.NOT_EQUALS, value: 'a' },
106
+ ];
107
+ expect((0, position_matcher_1.filterRows)([stubRow(null)], filters)).toEqual([]);
62
108
  });
63
109
  });
64
110
  describe('the published (compiled) position_matcher.js', () => {
@@ -74,4 +120,8 @@ describe('the published (compiled) position_matcher.js', () => {
74
120
  const kept = compiled.filterRows(rows, assetClassFilter(position_util_pb_1.PositionFilterOperator.EQUALS));
75
121
  expect(assetClasses(kept)).toEqual(['RATES']);
76
122
  });
123
+ test('has the LM-284 null rule', () => {
124
+ expect(compiled.matches(field_pb_1.FieldProto.PORTFOLIO_NAME, position_util_pb_1.PositionFilterOperator.NOT_EQUALS, 'a', null)).toBe(false);
125
+ expect(compiled.matches(field_pb_1.FieldProto.ASSET_CLASS, position_util_pb_1.PositionFilterOperator.NOT_EQUALS, 'FIXED_INCOME', null)).toBe(true);
126
+ });
77
127
  });
@@ -4,12 +4,28 @@
4
4
  * ASSET_CLASS EQUALS / NOT_EQUALS use the shared assetClassMatches rule;
5
5
  * every other field compares exactly. A null stored value never throws.
6
6
  */
7
+ import * as fs from 'fs';
8
+ import * as path from 'path';
9
+
7
10
  import { FieldProto } from '../../../fintekkers/models/position/field_pb';
8
11
  import { PositionFilterOperator } from '../../../fintekkers/models/position/position_util_pb';
9
12
  import { SecurityProto } from '../../../fintekkers/models/security/security_pb';
10
13
  import Security from '../security/security';
11
14
  import { FilterableRow, FilterSpec, filterRows, matches } from './position_matcher';
12
15
 
16
+ const PROTOS = path.resolve(__dirname, '../../../../../ledger-models-protos');
17
+
18
+ interface NullCase {
19
+ field: string;
20
+ operator: string;
21
+ filter: string;
22
+ expected: boolean;
23
+ }
24
+
25
+ const nullCases: NullCase[] = JSON.parse(
26
+ fs.readFileSync(path.join(PROTOS, 'fixtures/position_filter_null_cases.json'), 'utf-8'),
27
+ ).cases;
28
+
13
29
  function securityRow(assetClass: string): Security {
14
30
  const proto = new SecurityProto();
15
31
  proto.setAssetClass(assetClass);
@@ -61,7 +77,29 @@ describe('position_matcher', () => {
61
77
  expect(matches(FieldProto.MATURITY_DATE, PositionFilterOperator.MORE_THAN, 1, 2)).toBe(true);
62
78
  expect(matches(FieldProto.MATURITY_DATE, PositionFilterOperator.MORE_THAN, 2, 1)).toBe(false);
63
79
  expect(matches(FieldProto.PORTFOLIO_NAME, PositionFilterOperator.EQUALS, 'a', null)).toBe(false);
64
- expect(matches(FieldProto.PORTFOLIO_NAME, PositionFilterOperator.NOT_EQUALS, 'a', null)).toBe(true);
80
+ expect(matches(FieldProto.PORTFOLIO_NAME, PositionFilterOperator.NOT_EQUALS, 'a', null)).toBe(false);
81
+ });
82
+
83
+ // LM-284: NOT_EQUALS drops a null on every field but ASSET_CLASS. Cases
84
+ // come from ledger-models-protos/fixtures/position_filter_null_cases.json,
85
+ // shared with the Java, Python and Rust tests.
86
+ test('the shared null-cases fixture has its cases', () => {
87
+ expect(nullCases.length).toBeGreaterThanOrEqual(7);
88
+ });
89
+
90
+ test.each(nullCases)('$field $operator $filter on null -> $expected', (c) => {
91
+ const field = FieldProto[c.field as keyof typeof FieldProto];
92
+ const operator = PositionFilterOperator[c.operator as keyof typeof PositionFilterOperator];
93
+ expect(field).toBeDefined();
94
+ expect(operator).toBeDefined();
95
+ expect(matches(field, operator, c.filter, null)).toBe(c.expected);
96
+ });
97
+
98
+ test('filterRows NOT_EQUALS drops a row with a null on another field', () => {
99
+ const filters: FilterSpec[] = [
100
+ { field: FieldProto.PORTFOLIO_NAME, operator: PositionFilterOperator.NOT_EQUALS, value: 'a' },
101
+ ];
102
+ expect(filterRows([stubRow(null)], filters)).toEqual([]);
65
103
  });
66
104
  });
67
105
 
@@ -93,4 +131,13 @@ describe('the published (compiled) position_matcher.js', () => {
93
131
  const kept = compiled.filterRows(rows, assetClassFilter(PositionFilterOperator.EQUALS));
94
132
  expect(assetClasses(kept)).toEqual(['RATES']);
95
133
  });
134
+
135
+ test('has the LM-284 null rule', () => {
136
+ expect(
137
+ compiled.matches(FieldProto.PORTFOLIO_NAME, PositionFilterOperator.NOT_EQUALS, 'a', null),
138
+ ).toBe(false);
139
+ expect(
140
+ compiled.matches(FieldProto.ASSET_CLASS, PositionFilterOperator.NOT_EQUALS, 'FIXED_INCOME', null),
141
+ ).toBe(true);
142
+ });
96
143
  });
@@ -19,8 +19,10 @@ export interface FilterableRow {
19
19
  * ASSET_CLASS with EQUALS / NOT_EQUALS uses the shared assetClassMatches
20
20
  * rule, so a group code such as FIXED_INCOME matches its members and labels;
21
21
  * every other field compares exactly. A null or undefined stored value never
22
- * throws: EQUALS and the ordering operators drop the row, NOT_EQUALS keeps
23
- * it. An unknown operator matches nothing. Errors raised by row.getField
22
+ * throws and drops the row for every operator, except ASSET_CLASS with
23
+ * NOT_EQUALS, which keeps it (LM-281). The null check runs before the
24
+ * ASSET_CLASS branch, so it handles that asset-class exception itself
25
+ * (LM-284). An unknown operator matches nothing. Errors raised by row.getField
24
26
  * propagate (a row represents an absent value by returning null).
25
27
  */
26
28
  export function matches(
@@ -30,7 +32,7 @@ export function matches(
30
32
  storedValue: any,
31
33
  ): boolean {
32
34
  if (storedValue === null || storedValue === undefined) {
33
- return operator === PositionFilterOperator.NOT_EQUALS;
35
+ return field === FieldProto.ASSET_CLASS && operator === PositionFilterOperator.NOT_EQUALS;
34
36
  }
35
37
  if (
36
38
  field === FieldProto.ASSET_CLASS &&
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fintekkers/ledger-models",
3
3
  "todo": "Replace the version with build script version number",
4
- "version": "0.4.29",
4
+ "version": "0.4.30",
5
5
  "description": "ledger model protos ",
6
6
  "authors": [
7
7
  "David Doherty",