hosted-consolidation-advanced.test.mjs
213 lines 7.7 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 import { describe, it } from 'node:test';
2 import assert from 'node:assert/strict';
3 import {
4 mergeConsolidateRequestBodyWithBillingDefaults,
5 validateHostedSettingsConsolidationAdvanced,
6 hostedAdvancedFromBillingUser,
7 clampConsolidationInt,
8 } from '../lib/hosted-consolidation-advanced.mjs';
9 import { normalizeBillingUser, defaultUserRecord } from '../hub/gateway/billing-logic.mjs';
10
11 describe('hostedAdvancedFromBillingUser', () => {
12 it('returns defaults for null user', () => {
13 const d = hostedAdvancedFromBillingUser(null);
14 assert.equal(d.lookback_hours, 24);
15 assert.equal(d.max_events_per_pass, 200);
16 assert.equal(d.max_topics_per_pass, 10);
17 assert.equal(d.llm_max_tokens, 1024);
18 });
19
20 it('reads normalized billing fields', () => {
21 const d = hostedAdvancedFromBillingUser({
22 consolidation_lookback_hours: 72,
23 consolidation_max_events_per_pass: 50,
24 consolidation_max_topics_per_pass: 3,
25 consolidation_llm_max_tokens: 512,
26 });
27 assert.equal(d.lookback_hours, 72);
28 assert.equal(d.max_events_per_pass, 50);
29 assert.equal(d.max_topics_per_pass, 3);
30 assert.equal(d.llm_max_tokens, 512);
31 });
32 });
33
34 describe('mergeConsolidateRequestBodyWithBillingDefaults', () => {
35 const u = {
36 consolidation_lookback_hours: 48,
37 consolidation_max_events_per_pass: 120,
38 consolidation_max_topics_per_pass: 7,
39 consolidation_llm_max_tokens: 1536,
40 };
41
42 it('fills missing keys from billing user', () => {
43 const out = mergeConsolidateRequestBodyWithBillingDefaults({ passes: { consolidate: true } }, u);
44 assert.equal(out.lookback_hours, 48);
45 assert.equal(out.max_events_per_pass, 120);
46 assert.equal(out.max_topics_per_pass, 7);
47 assert.equal(out.llm.max_tokens, 1536);
48 assert.equal(out.passes.consolidate, true);
49 });
50
51 it('request body overrides billing defaults', () => {
52 const out = mergeConsolidateRequestBodyWithBillingDefaults(
53 { lookback_hours: 12, max_events_per_pass: 80, llm: { max_tokens: 256 } },
54 u,
55 );
56 assert.equal(out.lookback_hours, 12);
57 assert.equal(out.max_events_per_pass, 80);
58 assert.equal(out.max_topics_per_pass, 7);
59 assert.equal(out.llm.max_tokens, 256);
60 });
61
62 it('preserves extra llm keys from body', () => {
63 const out = mergeConsolidateRequestBodyWithBillingDefaults({ llm: { foo: 1 } }, u);
64 assert.equal(out.llm.foo, 1);
65 assert.equal(out.llm.max_tokens, 1536);
66 });
67 });
68
69 describe('validateHostedSettingsConsolidationAdvanced', () => {
70 it('accepts empty body', () => {
71 assert.equal(validateHostedSettingsConsolidationAdvanced({}).ok, true);
72 });
73
74 it('rejects lookback out of range', () => {
75 const r = validateHostedSettingsConsolidationAdvanced({ lookback_hours: 0 });
76 assert.equal(r.ok, false);
77 assert.match(r.error, /lookback_hours/);
78 });
79
80 it('rejects max_events_per_pass out of range (above)', () => {
81 const r = validateHostedSettingsConsolidationAdvanced({ max_events_per_pass: 10001 });
82 assert.equal(r.ok, false);
83 assert.match(r.error, /max_events_per_pass/);
84 });
85
86 it('rejects max_topics_per_pass out of range (below)', () => {
87 const r = validateHostedSettingsConsolidationAdvanced({ max_topics_per_pass: 0 });
88 assert.equal(r.ok, false);
89 assert.match(r.error, /max_topics_per_pass/);
90 });
91
92 it('rejects max_tokens out of range', () => {
93 const r = validateHostedSettingsConsolidationAdvanced({ llm: { max_tokens: 10 } });
94 assert.equal(r.ok, false);
95 assert.match(r.error, /max_tokens/);
96 });
97
98 it('rejects NaN / non-numeric lookback_hours', () => {
99 assert.equal(validateHostedSettingsConsolidationAdvanced({ lookback_hours: 'garbage' }).ok, false);
100 assert.equal(validateHostedSettingsConsolidationAdvanced({ lookback_hours: NaN }).ok, false);
101 });
102
103 it('rejects NaN max_events_per_pass', () => {
104 assert.equal(validateHostedSettingsConsolidationAdvanced({ max_events_per_pass: 'abc' }).ok, false);
105 });
106
107 it('rejects NaN max_topics_per_pass', () => {
108 assert.equal(validateHostedSettingsConsolidationAdvanced({ max_topics_per_pass: undefined }).ok, true,
109 'undefined fields should be skipped (not present)');
110 assert.equal(validateHostedSettingsConsolidationAdvanced({ max_topics_per_pass: null }).ok, false,
111 'null should fail as it is defined but not numeric');
112 });
113
114 it('rejects NaN llm.max_tokens', () => {
115 assert.equal(validateHostedSettingsConsolidationAdvanced({ llm: { max_tokens: 'foo' } }).ok, false);
116 });
117
118 it('accepts boundary values', () => {
119 assert.equal(
120 validateHostedSettingsConsolidationAdvanced({
121 lookback_hours: 8760,
122 max_events_per_pass: 10000,
123 max_topics_per_pass: 500,
124 llm: { max_tokens: 8192 },
125 }).ok,
126 true,
127 );
128 });
129
130 it('accepts minimum boundary values', () => {
131 assert.equal(
132 validateHostedSettingsConsolidationAdvanced({
133 lookback_hours: 1,
134 max_events_per_pass: 1,
135 max_topics_per_pass: 1,
136 llm: { max_tokens: 64 },
137 }).ok,
138 true,
139 );
140 });
141 });
142
143 describe('clampConsolidationInt', () => {
144 it('clamps to range', () => {
145 assert.equal(clampConsolidationInt(5000, 1, 8760, 24), 5000);
146 assert.equal(clampConsolidationInt(999999, 1, 8760, 24), 8760);
147 assert.equal(clampConsolidationInt('bad', 1, 10, 3), 3);
148 });
149
150 it('clamps below-min to min', () => {
151 assert.equal(clampConsolidationInt(-5, 1, 100, 50), 1);
152 });
153
154 it('uses fallback for undefined, NaN, Infinity (non-finite)', () => {
155 assert.equal(clampConsolidationInt(undefined, 1, 100, 50), 50);
156 assert.equal(clampConsolidationInt(NaN, 1, 100, 50), 50);
157 assert.equal(clampConsolidationInt(Infinity, 1, 100, 50), 50);
158 assert.equal(clampConsolidationInt(-Infinity, 1, 100, 50), 50);
159 });
160
161 it('clamps null (Number(null)===0) to min, not fallback', () => {
162 assert.equal(clampConsolidationInt(null, 1, 100, 50), 1);
163 });
164 });
165
166 describe('normalizeBillingUser — advanced field migration (old records)', () => {
167 it('adds default advanced fields to a record that lacks them', () => {
168 const old = { user_id: 'legacy', tier: 'plus', consolidation_enabled: true };
169 const u = normalizeBillingUser(old);
170 assert.equal(u.consolidation_lookback_hours, 24);
171 assert.equal(u.consolidation_max_events_per_pass, 200);
172 assert.equal(u.consolidation_max_topics_per_pass, 10);
173 assert.equal(u.consolidation_llm_max_tokens, 1024);
174 });
175
176 it('clamps out-of-range advanced values to valid range', () => {
177 const bad = {
178 user_id: 'clamped',
179 consolidation_lookback_hours: 99999,
180 consolidation_max_events_per_pass: -5,
181 consolidation_max_topics_per_pass: 0,
182 consolidation_llm_max_tokens: 50000,
183 };
184 const u = normalizeBillingUser(bad);
185 assert.equal(u.consolidation_lookback_hours, 8760);
186 assert.equal(u.consolidation_max_events_per_pass, 1);
187 assert.equal(u.consolidation_max_topics_per_pass, 1);
188 assert.equal(u.consolidation_llm_max_tokens, 8192);
189 });
190
191 it('preserves valid in-range advanced values', () => {
192 const good = {
193 user_id: 'preserved',
194 consolidation_lookback_hours: 72,
195 consolidation_max_events_per_pass: 150,
196 consolidation_max_topics_per_pass: 8,
197 consolidation_llm_max_tokens: 2048,
198 };
199 const u = normalizeBillingUser(good);
200 assert.equal(u.consolidation_lookback_hours, 72);
201 assert.equal(u.consolidation_max_events_per_pass, 150);
202 assert.equal(u.consolidation_max_topics_per_pass, 8);
203 assert.equal(u.consolidation_llm_max_tokens, 2048);
204 });
205
206 it('defaultUserRecord includes all four advanced fields', () => {
207 const u = defaultUserRecord('new_user');
208 assert.equal(u.consolidation_lookback_hours, 24);
209 assert.equal(u.consolidation_max_events_per_pass, 200);
210 assert.equal(u.consolidation_max_topics_per_pass, 10);
211 assert.equal(u.consolidation_llm_max_tokens, 1024);
212 });
213 });
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 1 day ago